/**
 * 定义命名空间 
 */
Ext.ns("divo")
Ext.ns("divo.data")
Ext.ns("divo.form")
Ext.ns("divo.grid")
Ext.ns("divo.layout")
Ext.ns("divo.panel")
Ext.ns("divo.menu")
Ext.ns("divo.utils")
Ext.ns("divo.tree")
Ext.ns("divo.misc")
 
Ext.ns("Ext.ux")

//EOP
;/**
 * 客户端持久存储
 */
divo.storeMode = 1 // 1-cookies 2-persistJS

if (divo.storeMode == 1) {
	Ext.apply(divo, {
		// save data in store
		save : function(name, value) {
			divo.setCookie(name, value)
		},
		find : function(name) {
			return divo.getCookie(name)
		},
		remove : function(name) {
			divo.delCookie(name)
		},
		clearRememberUserInfo : function() {
			divo.remove("user_remember")
			divo.remove("user_loginName")
			divo.remove("user_password")
		},
		setCookie : function(name, value) {
			var Days = 365
			var exp = new Date()
			exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000)
			document.cookie = name + "=" + escape(value) + ";expires="
					+ exp.toGMTString()
		},
		getCookie : function(name) {
			var arr = document.cookie.match(new RegExp("(^| )" + name
					+ "=([^;]*)(;|$)"))
			if (arr != null)
				return unescape(arr[2])
			return null
		},
		delCookie : function(name) {
			var exp = new Date()
			exp.setTime(exp.getTime() - 1)
			var cval = divo.getCookie(name)
			if (cval != null)
				document.cookie = name + "=" + cval + ";expires="
						+ exp.toGMTString()
		}
		
	})
}

if (divo.storeMode == 2) {
	divo.store = new Persist.Store('divo_default')

	Ext.apply(divo, {
		// save data in store
		save : function(name, value) {
			divo.store.set(name, value)
		},
		find : function(name) {
			var v = null
			divo.store.get(name, function(ok, val) {
				if (ok)
					v = val
			})
			return v
		},
		remove : function(name) {
			divo.store.remove(name)
		},
		clearRememberUserInfo : function() {
			divo.remove("user_remember")
			divo.remove("user_loginName")
			divo.remove("user_password")
		}
	})
}

// EOP
;/**
 * 应用程序上下文
 */
divo.AppContext = function(config) {
	Ext.apply(this, config)
	divo.AppContext.superclass.constructor.call(this)
}
Ext.extend(divo.AppContext, Ext.util.Observable, {
	user : null,
	funcPermissions : [],
	globalPermissions : [],
	isDebug : false,
	baseUrl : "media/",
	homeUrl : null,
	init : function() {
		this.homeUrl = window.location.href
		this.loadAppContext()
		this.loadPermissions()
	},
	isAdmin : function(userId) {
		if (userId !== undefined)
			return userId === 1;
		return false;
	},
	getDayMonthYearFormat : function() {
		return "Y.m.d";
	},
	getDatetimeFormat : function() {
		return "Y.m.d h:i a";
	},
	hasAppInstalled : function(app) {
		if (!this.apps) return false
		for (var i = 0; i < this.apps.length; i++) {
			if (this.apps[i].indexOf(app)>=0) {
				return true
			}
		}
		return false
	},
	loadAppContext : function() {
		Ext.Ajax.request({
			scope : this,
			url : '/appcontext',
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				this.user = {id : resp.userId, 
							  name : resp.userName, 
							  fullName : resp.userFullName,
							  email : resp.email}
				this.isDebug = resp.isDebug
				this.apps = resp.apps
				this.version = resp.version
			},
			failure : function(response, options) {
				divo.showError(response.responseText)
			}
		})
	},
	loadPermissions : function() {
		Ext.Ajax.request({
			scope : this,
			url : '/users/'+this.user.id+'/permissions/functional',
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				this.funcPermissions = resp.rows
			},
			failure : function(response, options) {
				divo.showError(response.responseText)
			}
		})
		Ext.Ajax.request({
			scope : this,
			url : '/users/'+this.user.id+'/permissions/global',
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				this.globalPermissions = resp.rows
			},
			failure : function(response, options) {
				divo.showError(response.responseText)
			}
		})
	}
})

divo.appContext = new divo.AppContext()

// EOP
;/**
 * 改写Ext组件的默认行为
 */
Ext.override(Ext.Component, {
	defaults : {
		border : false
	},
	//border : false,
	getAppContext : function() {
		return divo.appContext
	},
	getUserId : function() {
		return divo.getUserId()
	},
	getUserFullName : function() {
		return divo.getUserFullName()
	},
	myId : function(id) {
		return this.id + "-" + id
	},
	subscribe : function(name, callback, scope) {
		if (!this.pagebusSubs)
		    this.pagebusSubs = []
		this.pagebusSubs.push(divo.subscribe(name, callback, scope))
	},
	unsubscribe : function() {
		if (!this.pagebusSubs) return
		
		for (var i = 0; i < this.pagebusSubs.length; i++)
			divo.unsubscribe(this.pagebusSubs[i])
	},
	publish : function(name, message) {
		divo.publish(name, message)
	},
	alert : function(msg) {
		divo.StatusBar.clear()
		divo.showError(msg)
	},
    say : function(msg) {
        divo.StatusBar.say(msg)
	},
    sayWait : function(msg) {
        divo.StatusBar.say('正在'+msg+'...')
	},
    sayClear : function(msg) {
        divo.StatusBar.clear()
	},
	menuItemId : null,
    //有新建操作权限吗？
    canCreate : function() {
        return divo.canCreate(this.menuItemId)
    },
    //有修改操作权限吗？
    canUpdate : function() {
        return divo.canUpdate(this.menuItemId)
    },
    //有删除操作权限吗？
    canDelete : function() {
        return divo.canDelete(this.menuItemId)
    },
    //获取企业会员信息
	getCorpUserInfo : function() {
		var corpUserInfo = null
		Ext.Ajax.request({
			scope : this,
			url : "/xf/corpusers/"+this.getUserId()+"/info",
			async : false,
			method : 'GET',
			success : function(response, options) {
				corpUserInfo = Ext.decode(response.responseText)
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		return corpUserInfo
	},
	//判断当前用户是否为二级企业会员
	isSecondaryCorpUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isSecondaryCorpUser
	},
	//判断当前用户是否为企业会员
	isCorpUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser
	},
	//判断当前用户是否为"检测公司"企业会员
	isCheckerUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.checker_id
	},
	isCheckerUserAndAuditPassed : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.checker_id && corpUserInfo.audit_passed
	},
	//判断当前用户是否为"工程公司"企业会员
	isProjecterUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.projecter_id
	},
	//判断当前用户是否为"产品公司"企业会员
	isProducerUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.producer_id
	},
	//判断当前用户是否为"维修公司"企业会员
	isMainterUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.mainter_id
	},
	//判断当前用户是否为"咨询公司"企业会员
	isAdvisorUser : function() {
		var corpUserInfo = this.getCorpUserInfo()
		return corpUserInfo && corpUserInfo.isCorpUser && corpUserInfo.advisor_id
	},
	//获取企业会员所在企业详细资料
	getCorpInfo : function() {
		var data = this.getCorpUserInfo()
		
		var corpType,corpId
		if (data.checker_id) {
			corpType = "checker"
			corpId = data.checker_id
		}
		if (data.producer_id) {
			corpType = "producer"
			corpId = data.producer_id
		}
		if (data.projecter_id) {
			corpType = "projecter"
			corpId = data.projecter_id
		}
		if (data.mainter_id) {
			corpType = "mainter"
			corpId = data.mainter_id
		}
		if (data.advisor_id) {
			corpType = "advisor"
			corpId = data.advisor_id
		}
		if (!corpType) return null
		corpInfo = null
		Ext.Ajax.request({
			scope : this,
			url : "/xf/"+corpType+"s/"+corpId,
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				corpInfo = resp.data
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		return corpInfo
	}
})

Ext.override(Ext.form.BasicForm, {
	getObjectValues : function() {
		var vv = this.getValues()
		this.items.each(function(f){
             if (f.xtype=='numberfield') {
                 vv[f.name] = parseFloat(vv[f.name]||0)
             }  
		})     
		return vv
	} //解决将NumericField没有转换为数值的问题
})

Ext.override(Ext.DataView, {
	refresh : function() {
		this.clearSelections(false, true)
		this.el.update("")
		var r = this.store.getRange()
		if (r.length < 1) {
			this.el.update(this.emptyText)
			this.all.clear()
			return
		}
		this.tpl.overwrite(this.el, this.collectData(r, 0))
		this.all.fill(Ext.query(this.itemSelector, this.el.dom))
		this.updateIndexes(0)
		this.fireEvent("afterRefresh", this)
	}
})

Ext.override(Ext.Panel, {
	setTabTitleAndIcon : function(title, iconStyle) {
		var ic = this.iconCls
		this.setIconClass(iconStyle)
		var el = this.ownerCt.getTabEl(this)
		if (el) {
			var elc = Ext.fly(el).child("span.x-tab-strip-text")
			elc.update(title)
			if (iconStyle) {
				elc.replaceClass(ic, iconStyle)
			}
		}
	},
	setTabTitleAndImage : function(title, image) {
		var ic = this.iconCls
		var el = this.ownerCt.getTabEl(this)
		if (el) {
			var elc = Ext.fly(el).child("span.x-tab-strip-text")
			var s = String
					.format("background-image: url(img/tree/{0})", image)
			elc.removeClass(ic)
			elc.applyStyles(s)
			elc.update(title)
		}
	}
})

//关闭按钮的handler直接可以这样使用：
//			buttons : [{
//				text : "关闭",
//				handler : this.onCancel,
//				scope : this
//			}]
//另外，cancelAction 也可以像 closeAction一样设置，如：
//
//test.window.MyProfileWindow = Ext.extend(Ext.Window, {
//	cancelAction : "destroy",
//	closeAction : "destroy",
//	initComponent : function() {
//  。。。
//  
Ext.override(Ext.Window, {
	cancelAction : "destroy",
	afterRender : function() {
		Ext.Window.superclass.afterRender.apply(this, arguments)
		if (Ext.isGecko) {
			this.on("show", function(d) {
				var div = Ext.get(d.el)
				div.setStyle("position", "fixed")
			})
		}
	},
	onCancel : function() {
		this[this.cancelAction]()
	}
})

Ext.override(Ext.menu.Menu, {
	showButtons : function(_mask, _mode) {
		for (var i = 0, len = this.items.length;i < len; i++) {
			var item = this.items.itemAt(i)
			var mode = item.mode
			var mask = item.mask
			var state = !mask || ((_mask & mask) && (!_mode || (_mode & mode)))
			item[state ? "show" : "hide"]()
		}
	}
})

Ext.override(Ext.Toolbar, {
	showButtons : function(_51, _52) {
		for (var i = 0, len = this.items.length; i < len; i++) {
			var _54 = this.items.itemAt(i);
			var _55 = _54.mode;
			var _56 = _54.mask;
			var _57 = !_56 || ((_51 & _56) && (!_52 || (_52 & _55)));
			_54[_57 ? "show" : "hide"]();
		}
	},
	addField : function(_58) {
		var td = this.nextBlock();
		_58.render(td);
		this.items.add(_58);
		return _58;
	}
});
Ext.Toolbar.Item.prototype.constructor = function(el, _5b) {
	Ext.apply(this, _5b);
	this.el = Ext.getDom(el);
	this.id = Ext.id(this.el);
	this.hidden = false;
};
Ext.Toolbar.TextItem.prototype.constructor = function(t) {
	var s = document.createElement("span");
	s.className = "ytb-text";
	s.innerHTML = t.text ? t.text : t;
	Ext.Toolbar.TextItem.superclass.constructor.call(this, s, t);
};

(function() {
	var T = Ext.Toolbar;
	T.Item = function(el, _60) {
		Ext.apply(this, _60);
		this.el = Ext.getDom(el);
		this.id = Ext.id(this.el);
		this.hidden = false;
	};
	T.Item.prototype = {
		getEl : function() {
			return this.el;
		},
		render : function(td) {
			this.td = td;
			td.appendChild(this.el);
		},
		destroy : function() {
			if (this.td && this.td.parentNode) {
				this.td.parentNode.removeChild(this.td);
			}
		},
		show : function() {
			this.hidden = false;
			this.td.style.display = "";
		},
		hide : function() {
			this.hidden = true;
			this.td.style.display = "none";
		},
		setVisible : function(_62) {
			if (_62) {
				this.show();
			} else {
				this.hide();
			}
		},
		focus : function() {
			Ext.fly(this.el).focus();
		},
		disable : function() {
			Ext.fly(this.td).addClass("x-item-disabled");
			this.disabled = true;
			this.el.disabled = true;
		},
		enable : function() {
			Ext.fly(this.td).removeClass("x-item-disabled");
			this.disabled = false;
			this.el.disabled = false;
		}
	};
	Ext.reg("tbitem", T.Item);
	T.Separator = function(_63) {
		var s = document.createElement("span");
		s.className = "ytb-sep";
		T.Separator.superclass.constructor.call(this, s, _63);
	};
	Ext.extend(T.Separator, T.Item, {
		enable : Ext.emptyFn,
		disable : Ext.emptyFn,
		focus : Ext.emptyFn
	});
	Ext.reg("tbseparator", T.Separator);
	T.Spacer = function(_65) {
		var s = document.createElement("div");
		s.className = "ytb-spacer";
		T.Spacer.superclass.constructor.call(this, s, _65);
	};
	Ext.extend(T.Spacer, T.Item, {
		enable : Ext.emptyFn,
		disable : Ext.emptyFn,
		focus : Ext.emptyFn
	});
	Ext.reg("tbspacer", T.Spacer);
	T.Fill = Ext.extend(T.Spacer, {
		render : function(td) {
			td.style.width = "100%";
			T.Fill.superclass.render.call(this, td);
		}
	});
	Ext.reg("tbfill", T.Fill);
	T.TextItem = function(t) {
		var s = document.createElement("span");
		s.className = "ytb-text";
		s.innerHTML = t.text ? t.text : t;
		T.TextItem.superclass.constructor.call(this, s, t);
	};
	Ext.extend(T.TextItem, T.Item, {
		enable : Ext.emptyFn,
		disable : Ext.emptyFn,
		focus : Ext.emptyFn
	});
	Ext.reg("tbtext", T.TextItem);
	T.Button = Ext.extend(Ext.Button, {
		hideParent : true,
		onDestroy : function() {
			T.Button.superclass.onDestroy.call(this);
			if (this.container) {
				this.container.remove();
			}
		}
	});
	Ext.reg("tbbutton", T.Button);
	T.SplitButton = Ext.extend(Ext.SplitButton, {
		hideParent : true,
		onDestroy : function() {
			T.SplitButton.superclass.onDestroy.call(this);
			if (this.container) {
				this.container.remove();
			}
		}
	});
	Ext.reg("tbsplit", T.SplitButton);
	T.MenuButton = T.SplitButton;
})();

// 中文语言包(ext-lang-zh_CN.js)补丁
if (Ext.PagingToolbar) {
	Ext.apply(Ext.PagingToolbar.prototype, {
		beforePageText : "第",
		afterPageText : "页共 {0} 页",
		firstText : "第一页",
		prevText : "前一页",
		nextText : "下一页",
		lastText : "最后页",
		refreshText : "刷新",
		displayMsg : "显示{0}-{1}共{2}条",
		emptyMsg : '共0条'
	})
}
if (Ext.DatePicker) {
	Ext.apply(Ext.DatePicker.prototype, {
		todayText : "今天",
		minText : "日期在最小日期之前",
		maxText : "日期在最大日期之后",
		disabledDaysText : "",
		disabledDatesText : "",
		monthNames : Date.monthNames,
		dayNames : Date.dayNames,
		nextText : '下月 (Control+Right)',
		prevText : '上月 (Control+Left)',
		monthYearText : '选择一个月 (Control+Up/Down 来改变年)',
		todayTip : "{0} (空格键选择)",
		format : "y年m月d日"
	})
}
if (Ext.grid.GroupingView) {
	Ext.apply(Ext.grid.GroupingView.prototype, {
		groupByText : '分组',
		showGroupsText : '分组有效'
	})
}
if(Ext.grid.GridView){
   Ext.apply(Ext.grid.GridView.prototype, {
      sortAscText  : "升序",
      sortDescText : "降序",
      lockText     : "锁列",
      unlockText   : "解锁列",
      columnsText  : "列"
   });
}

//Thanks: http://extjs.com/forum/showthread.php?t=34819
if(Ext.menu){
    Ext.apply(Ext.menu.TextItem.prototype, {
        setText : function(text){
            this.text = text;
            if(this.rendered){
                this.el.update(text);
                this.parentMenu.autoWidth();
            }
        }
    });
} 

//http://extjs.com/forum/showthread.php?t=36474
//本人修改有修改：可以指定searchField
Ext.override(Ext.form.ComboBox, {
    doQuery : function(q, forceAll){
        if(q === undefined || q === null){
            q = '';
        }
        var qe = {
            query: q,
            forceAll: forceAll,
            combo: this,
            cancel:false
        };
        if(this.fireEvent('beforequery', qe)===false || qe.cancel){
            return false;
        }
        q = qe.query;
        forceAll = qe.forceAll;
        if(forceAll === true || (q.length >= this.minChars)){
            if(this.lastQuery !== q){
                // change: Durlabh's change start
                var lastQuery = this.lastQuery;
                // change:
                this.lastQuery = q;
                if(this.mode == 'local'){
                    this.selectedIndex = -1;
                    if(forceAll){
                        this.store.clearFilter();
                    }else{
                        if (this.searchField)  
                            //例如：下拉显示值为：
                            // 1 优先
                            // 2 一般
                            // 3 普通
                            //用户可以直接输入1或2或3就能选择，但复选框中仍然显示中文名称。
                            //此时,就要将searchField设置为数字对应的字段名。
                            this.store.filter(this.searchField, q);
                        else    
                            this.store.filter(this.displayField, q);
                    }
                    this.onLoad();
                }else{
                    // change: Durlabh's change start
                    var storeCount;
                    if(this.store.snapshot) {
                        storeCount = this.store.snapshot.length;
                    } else {
                        storeCount = this.store.getCount();
                    }
                    if(this.store.lastOptions && this.store.lastOptions.params) {
                        var lastQuery = this.store.lastOptions.params[this.queryParam];
                    }
                    if(lastQuery != null
                            && q.length >= lastQuery.length
                            && q.substr(0, lastQuery.length) == lastQuery
                            && this.store.getTotalCount() == storeCount) {

                        this.selectedIndex = -1;
                        this.store.filter(this.displayField, q);                        
                        this.onLoad();
                    }
                    else {
                        this.store.baseParams[this.queryParam] = q;
                        this.store.load({
                            params: this.getParams(q)
                        });
                    }
                    // change:
                    this.expand();
                }
            }else{
                this.selectedIndex = -1;
                this.onLoad();
            }
        }
    }
});

//nodeConfigFn： 调用该方法构建节点对象,并实现服务器返回的数据映射到标准节点属性
Ext.override(Ext.tree.TreeLoader,{
    processResponse : function(response, node, callback){
        var json = response.responseText;
        try {
            var o = eval("("+json+")");
            node.beginUpdate();
            for(var i = 0, len = o.length; i < len; i++){
            	if (this.nodeConfigFn)
                	var n = this.createNode(this.nodeConfigFn(o[i]));
                else	
                	var n = this.createNode(o[i]);
                if(n){
                    node.appendChild(n);
                }
            }
            node.endUpdate();
            if(typeof callback == "function"){
                callback(this, node);
            }
        }catch(e){
            this.handleFailure(response);
        }
    }
})

//Thanks: http://extjs.com/forum/showthread.php?t=22037
Ext.EventManagerFix = function(){
    var docReadyEvent, docReadyProcId, docReadyState = false;
    var E = Ext.lib.Event;
    var D = Ext.lib.Dom;


    var fireDocReady = function(){
        if(!docReadyState){
            docReadyState = true;
            Ext.isReady = true;
            if(docReadyProcId){
                clearInterval(docReadyProcId);
            }
            if(Ext.isGecko || Ext.isOpera) {
                document.removeEventListener("DOMContentLoaded", fireDocReady, false);
            }
            if(Ext.isIE){
                var defer = document.getElementById("ie-deferred-loader");
                if(defer){
                    defer.onreadystatechange = null;
                    defer.parentNode.removeChild(defer);
                }
            }
            if(docReadyEvent){
                docReadyEvent.fire();
                docReadyEvent.clearListeners();
            }
        }
    };

    var initDocReady = function(){
        docReadyEvent = new Ext.util.Event();
        if(Ext.isGecko || Ext.isOpera) {
            document.addEventListener("DOMContentLoaded", fireDocReady, false);
        }else if(Ext.isIE){
            document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
            var defer = document.getElementById("ie-deferred-loader");
			var f = function() {
                if(defer.readyState == "complete"){
                    fireDocReady();
                }
			}
            defer.onreadystatechange = function(){
				f.defer(1000);
            };
        }else if(Ext.isSafari){
            docReadyProcId = setInterval(function(){
                var rs = document.readyState;
                if(rs == "complete") {
                    fireDocReady();
                 }
            }, 10);
        }
        // no matter what, make sure it fires on load
        E.on(window, "load", fireDocReady);
    };

 
    var pub = {
        onDocumentReady : function(fn, scope, options){
            if(docReadyState){ // if it already fired
                docReadyEvent.addListener(fn, scope, options);
                docReadyEvent.fire();
                docReadyEvent.clearListeners();
                return;
            }
            if(!docReadyEvent){
                initDocReady();
            }
            docReadyEvent.addListener(fn, scope, options);
        }
    };
    return pub;
}();

//Thanks: http://extjs.com/forum/showthread.php?t=17396&page=2
Ext.override(Ext.layout.FormLayout, {
    renderItem : function(c, position, target){
        if(c && !c.rendered && c.isFormField && c.inputType != 'hidden'){
            var args = [
                   c.id, c.fieldLabel,
                   c.labelStyle||this.labelStyle||'',
                   this.elementStyle||'',
                   typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
                   (c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
                   c.clearCls || 'x-form-clear-left' 
            ];
            if(typeof position == 'number'){
                position = target.dom.childNodes[position] || null;
            }
            c.actionMode = 'formItem';
            if(position){
                c.formItem = Ext.get(this.fieldTpl.insertBefore(position, args));
            }else{
                c.formItem = Ext.get(this.fieldTpl.append(target, args));
            }
            c.render('x-form-el-'+c.id);
        }else {
            Ext.layout.FormLayout.superclass.renderItem.apply(this, arguments);
        }
    }
});

//Thanks: http://extjs.com/forum/showthread.php?t=47607
Ext.override( Ext.FormPanel ,{

    /**
     * Starts monitoring of the valid state of this form. Usually this is done by passing the config
     * option "monitorValid"
     */
    startMonitoring : function(){
        if(!this.bound){
            this.bound = true;
            if(!this.monitorTask){  //Make sure only one is running
               this.monitorTask = Ext.TaskMgr.start({
                   run : this.bindHandler,
                   interval : this.monitorPoll || 200,
                   scope: this
               });
            }
        }
    },

    /**
     * Stops monitoring of the valid state of this form
     */
    stopMonitoring : function(){
        this.bound = false;
        if(this.monitorTask){
           Ext.TaskMgr.stop(this.monitorTask);
           this.monitorTask = null;
        }
    }
}); 

Ext.onReady = Ext.EventManagerFix.onDocumentReady;
//EOP
;//Thanks: http://extjs.com/forum/showthread.php?t=19868&page=2
Ext.ToolTip.prototype.onTargetOver =
	Ext.ToolTip.prototype.onTargetOver.createInterceptor(function(e) {
		this.baseTarget = e.getTarget()
	})
	
Ext.ToolTip.prototype.onMouseMove =
	Ext.ToolTip.prototype.onMouseMove.createInterceptor(function(e) {     	    
	    if (this.baseTarget != null)
	    {  	   
		    if (!e.within(this.baseTarget)) {
			    this.onTargetOver(e);
			    return false;
		    }
		} else return false;
	})

	

	;/**
 * 配置ExtJS组件
 */
Ext.BLANK_IMAGE_URL = "media/js/ext/resources/images/default/s.gif";

Ext.QuickTips.init()
Ext.apply(Ext.QuickTips.getQuickTip(), {
	showDelay : 250,
	hideDelay : 300,
	dismissDelay : 0
}); // don't automatically hide quicktip

Ext.form.Field.prototype.msgTarget = 'under'

Ext.Ajax.defaultHeaders = {
	'accept' : 'application/json'
};
Ext.lib.Ajax.defaultPostHeader = 'application/json';

Ext.Msg.minWidth = 200;

//Thanks: http://extjs.com/forum/showthread.php?p=167921
Ext.TabPanel.prototype.layoutOnTabChange = true; 

//Thanks: http://extjs.com/forum/showthread.php?t=8051
Ext.Ajax.timeout=150000 //150秒
//EOP
;/**
 * 应用程序Viewport
 */
divo.App = Ext.extend(Ext.Viewport, {
	//msgCt : null,
	initApp : function() {
		this.getAppContext().init();
	},
	showLoading : function() {
		divo.showLoading()
	},
	hideLoading : function() {
		divo.hideLoading()
	},
	setAppContext : function(obj) {
		this.appContext = obj;
	}
});

// EOP
;/**
 * 为了调用而提供的快捷方式
 */
Ext.apply(divo, {
	//必输字段标记
    required : '<span style="font-size: 16px;color:red">*</span>',
    //加载提示
	loadMask : {
		msg : '',
		msgCls : 'x-mask-loading'
	},
	// 防止IFRAME销毁后仍然占用内存
	// Thanks: http://extjs.com/forum/showthread.php?t=11406
	fixIFrame : function(o, p) {
		var iFrame = p.getEl().dom
		if (iFrame.src) {
			iFrame.src = "javascript:false"
		}
	},
	// 方便调用PageBus
	subscribe : function(name, callback, scope) {
		return window.PageBus.subscribe(name, scope, callback, null)
	},
	publish : function(name, message) {
		window.PageBus.publish(name, message);
	},
	unsubscribe : function(sub) {
		window.PageBus.unsubscribe(sub)
	},
	// 加载提示
	showLoading : function() {
		Ext.get('divo-loading-msg').show()
	},
	hideLoading : function() {
		Ext.get('divo-loading-msg').hide()
	},
	//循环等待执行funcRun方法，直到funcStopWaiting返回值为真
	waitFor : function(funcStopWaiting, funcRun, scope){
	    var task = { 
	        run : function(){
	            if (funcStopWaiting.call(scope || this)){
	                funcRun.call(scope || this)
	                return false
	            }            
	        },
	        interval: 100	
	    }
	    if (!(task.run()===false)){
	        if (!task.duration && !task.repeat ){task.repeat=300}    
	        Ext.TaskMgr.start(task)
	    }
	},
	getUserId : function() {
		if (divo.appContext.user)
			return divo.appContext.user.id
		return null	
	},
	getUserFullName : function() {
		if (divo.appContext.user)
			return divo.appContext.user.fullName
		return null	
	},
	isAdmin : function() {
		var userId = divo.getUserId()
		return userId && userId == 1 
	},
	//模板开头和结尾
	t1 : '<tpl for=\".\">',
    t2 : '</tpl>',

    //当前用户是否具有指定全局权限
    hasPermission : function(name) {
        var ps = divo.appContext.globalPermissions
        for (var i=0; i < ps.length; i++) {
        	if (ps[i].name==name) return true
        }
        return false
    },
    //判断当前用户的菜单项操作权限
    isPermitted : function(menuItemId,pValue) {
        var ps = divo.appContext.funcPermissions
        var p
        for (var i=0; i < ps.length; i++) {
        	if (ps[i].item_id==menuItemId) {
        		p = ps[i].permission
        		break
        	}
        }
        return p && ((p & pValue) == pValue)
    },
    //有新建操作权限吗？
    canCreate : function(menuItemId) {
        return divo.isPermitted(menuItemId,1) //001
    },
    //有修改操作权限吗？
    canUpdate : function(menuItemId) {
        return divo.isPermitted(menuItemId,2) //010
    },
    //有删除操作权限吗？
    canDelete : function(menuItemId) {
        return divo.isPermitted(menuItemId,4) //100
    },
    //获取指定url的菜单项完整信息
    getMenuItem : function(url) {
		var item = null
		Ext.Ajax.request({
			scope : this,
			url : "/menus/items/url/"+url,
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					item = resp.data
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		return item
    },
    //用程序方式调用菜单项
    callMenuItem : function(url,extraInfo) {
    	var item = divo.getMenuItem(url)
    	   
		this.publish("j.selectMenuItem", {
			id : item.id, 
			name : item.name,
			close : extraInfo && extraInfo.close!=undefined?extraInfo.close:true,
			tabId : extraInfo && extraInfo.tabId!=undefined?extraInfo.tabId:"",
			title : extraInfo && extraInfo.title!=undefined?extraInfo.title:"",
			extraInfo : extraInfo
		})
    },
	/**
	 * Shorthand way of creating a toast window as an alert in one line.
	 * @config {String} title 默认为“通知”
	 * @config {String} iconCls 默认为“information”，还可传入“error”
	 * @config {Boolean} autoDestroy 默认为false
	 * @config {int} hideDelay 默认为 5000，autoDestroy为true时有效  
 	 */
	toastAlert : function(config) {
	    the_title = typeof(config.title) != 'undefined' ? config.title : '通知'
	    the_icon = typeof(config.iconCls) != 'undefined' ? config.iconCls : 'information'
	    the_autoDestroy = typeof(config.autoDestroy) != 'undefined' ? config.autoDestroy : false
	    
	    // Create the toast window
	    new Ext.ux.ToastWindow({
	        title: the_title,
	        html: config.message,
	        iconCls: the_icon,
	        autoDestroy: the_autoDestroy,
	        hideDelay: config.hideDelay 
	    }).show(document)
	} 

})
// EOP
;/**
 * 常用图标路径
 */
divo.iconUrl = divo.appContext.baseUrl + "images/"

Ext.apply(divo,
{
	iconAdd : divo.iconUrl + 'divo/add.gif',
	iconEdit :  divo.iconUrl + 'divo/edit.gif',
	iconDelete : divo.iconUrl + 'divo/delete.png',
	iconExpandAll : divo.iconUrl + 'divo/expandall.gif',
	iconCollapseAll : divo.iconUrl + 'divo/collapseall.gif',
	iconCollapseAll2 : divo.iconUrl + 'divo/collapseall2.png',
	iconSearch : divo.iconUrl + 'divo/search.gif',
	iconSave : divo.iconUrl + 'divo/save.gif',
	iconRefresh : divo.iconUrl + 'divo/refresh.gif',
	iconPublish : divo.iconUrl + 'divo/publish.gif'
	
})
//EOP


;/**
 * 显示后台完整的错误信息
 */
divo.ErrorForm = function() {
	/* ----------------------- private属性 ----------------------- */
	var win, errorMsg

	/* ----------------------- private方法 ----------------------- */
	// 创建窗口
	function createWindow(title) {
		if (win)
			return

		win = new Ext.Window({
			id : 'j-error-win',
			iconCls: 'icon-win',
			title : title,
			width : 500,
			height : 400,
			buttonAlign : 'right',
			closable : true,
			closeAction : 'hide',
			modal : true,
			maximizable : true,
			layout : "fit",
			items : [{
				xtype : 'iframepanel',
				html : errorMsg
			}]

		})
	}

	/* ----------------------- public方法 ----------------------- */
	return {

		/**
		 * 显示对话框
		 */
		show : function(_errorMsg,_errorType) {
			errorMsg = _errorMsg?_errorMsg:'<br /><h3>服务器出现故障，请稍候再试。</h3>'

			createWindow('错误：' + (_errorType==undefined?'服务器引起':_errorType))
			win.show()
		}

	} // return

}()

divo.showError = divo.ErrorForm.show

// EOP

;/**
 * 主页顶部状态条管理
 */
divo.StatusBar = function() {
	/* ----------------------- private变量 ----------------------- */
	var bar, barMsg;

	/* ----------------------- private方法 ----------------------- */
	// 渐进渐出效果
	function slideIn(duration) {
		bar.slideIn('t').pause(duration).ghost("t", {
			remove : false
		});
	}
	
	//创建HTML标记
	function createHTML() {
		var html = [
		'<div id="status-bar" class="x-hidden">',
		'  <div id="status-msg" class="status-text"></div>',
		'</div>'];
		 
		Ext.DomHelper.append(document.body, {
			html : html.join('')
		});
	}

	/* ----------------------- public方法 ----------------------- */
	return {

		/**
		 * 初始化
		 */
		init : function() {
			createHTML();
			
			bar = Ext.get('status-bar');
			barMsg = Ext.get('status-msg');
			//Ext.get('status-bar-close').on('click', function() {
			//	this.clear();
			//},this);
			bar.hide();
		},
		/**
		 * 隐藏状态条
		 */
		clear : function() {
			bar.hide();
		},
		/**
		 * 显示操作成功信息
		 */
		say : function(msg) {
			bar.show();
			barMsg.update(msg);
			if (!msg.endsWith("..."))
				slideIn(5);
		}

	}; // return

}();

divo.StatusBar.init()

// EOP

;/**
 * 个人信息保存和恢复
 */
Ext.apply(divo, {
	/**
	 * 保存个人信息。
	 * 
	 * data数据格式举例：
	 * {
	 *		userId : 1,
	 *		msgCode : 'pp',
	 *		msgValue : 'vv'
	 *	}
	 */
	saveProfile : function(data,async) {
		if (!data.userId) return
		
		Ext.Ajax.request({
			async : async?async:false,
			url : "/myprofile/"+data.userId,
			method : 'POST',
			jsonData : {msg_code: data.msgCode, msg_value: data.msgValue},
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				if (!resp.success) {
					divo.say(resp.errors.reason)
				}
			},
			failure : function(response, options) {
				divo.showError(response.responseText);
			}
		});
	},
	saveProfileAsync : function(data) {
		this.saveProfile(data,true)
	},
	/**
	 * 恢复保存的个人信息值
	 * 
	 * 返回值数据格式举例：{msgValue:'1'}
	 */
	restoreProfile : function(callback, userId, msgCode) {
		Ext.Ajax.request({
			scope : this,
			async : false,
			url : "/myprofile/"+userId+"/"+msgCode,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				if (resp.success) {
					callback({msgValue:resp.data.msg_value})
				} else {
					callback(null)
				}
			},
			failure : function(response, options) {
				callback(null)
			}
		});
	}

})
// EOP
;/**
 * 在数据库中持久保存状态
 */
divo.StateProvider = function(config) {
	divo.StateProvider.superclass.constructor.call(this);
	Ext.apply(this, config);
};

Ext.extend(divo.StateProvider, Ext.state.Provider, {
	// private
	set : function(name, value) {
		// 避免多保存不必要的状态
		if (name.length > 3 && name.substr(0, 3) == 'ext')
			return;

		if (value == undefined || value === null || value=='') {
			//this.clear(name);
			return;
		}
		var toSave = true
		if (this.lastUserId) {
			toSave = !(this.lastUserId==divo.getUserId() && 
			    this.lastMsgCode==name &&
			    this.lastMsgValue==value)
		}
		if (toSave) {
			divo.saveProfileAsync({
				userId : divo.getUserId(),
				msgCode : name,
				msgValue : this.encodeValue(value)
			})
			this.lastUserId = divo.getUserId()
			this.lastMsgCode = name
			this.lastMsgValue = value
		}	
		divo.StateProvider.superclass.set.call(this, name, value);
	},

//	// private
//	clear : function(name) {
//		divo.saveProfileAsync({
//			userId : divo.getUserId(),
//			msgCode : name,
//			msgValue : ''
//		})
//		divo.StateProvider.superclass.clear.call(this, name);
//	},

	// private
	get : function(name, defaultValue) {
		// 避免多保存不必要的状态
		if (name.length > 3 && name.substr(0, 3) == 'ext')
			return undefined;

		var result;
		divo.restoreProfile(function(retValue) {
			result = retValue
		}, divo.getUserId(), name)
		if (!result) {
			this.set(name, defaultValue);
			return this.decodeValue(defaultValue);
		}
		return this.decodeValue(result.msgValue);
	}

});

Ext.override(Ext.Component, {
	stateful : false
}); // Thanks: http://extjs.com/forum/showthread.php?t=15675

Ext.state.Manager.setProvider(new divo.StateProvider());

// EOP
;/**
 * SimpleListStore：数据必须在构造时加载（以后不能再重新加载?)
 * 
 * 数据源举例:
 *	[{versionId:1,versionNumber:'1.0',comments:'评论1',createdDate:new Date(),userName:'user1'},
 *	 {versionId:2,versionNumber:'2.0',comments:'评论2',createdDate:new Date(),userName:'user2'}
 *	]
 */
divo.data.SimpleListStore = function(config) {
	divo.data.SimpleListStore.superclass.constructor.call(this, {
		reader : new Ext.data.JsonReader({
			id : config.id
		}, Ext.data.Record.create(config.fields)),
		proxy : new Ext.data.MemoryProxy(config.data)
	});
	this.load()
}

Ext.extend(divo.data.SimpleListStore, Ext.data.Store)
//EOP
;/**
 * ArrayStore：数据在构造时可以加载，也可以重新加载
 */
divo.data.ArrayStore = function(config) {
	divo.data.ArrayStore.superclass.constructor.call(this, {
		reader : new Ext.data.ArrayReader({}, Ext.data.Record.create(config.fields))
	})
	this.loadData(config.data||[])
};

Ext.extend(divo.data.ArrayStore, Ext.data.Store)
// EOP
;(function(){var EV=Ext.lib.Event;Ext.ux.ManagedIFrame=function(){var args=Array.prototype.slice.call(arguments,0),el=Ext.get(args[0]),config=args[0];if(el&&el.dom&&el.dom.tagName=="IFRAME"){config=args[1]||{};}else{config=args[0]||args[1]||{};el=config.autoCreate?Ext.get(Ext.DomHelper.append(config.autoCreate.parent||document.body,Ext.apply({tag:"iframe",src:(Ext.isIE&&Ext.isSecure)?Ext.SSL_SECURE_URL:""},config.autoCreate))):null;}if(!el||el.dom.tagName!="IFRAME"){return el;}el.dom.name||(el.dom.name=el.dom.id);el.dom.mifId=el.dom.id;this.addEvents({"focus":true,"blur":true,"unload":true,"domready":true,"documentloaded":true,"exception":true,"message":true});if(config.listeners){this.listeners=config.listeners;Ext.ux.ManagedIFrame.superclass.constructor.call(this);}Ext.apply(el,this);el.addClass("x-managed-iframe");if(config.style){el.applyStyles(config.style);}el._maskEl=el.parent(".x-managed-iframe-mask")||el.parent().addClass("x-managed-iframe-mask");Ext.apply(el,{disableMessaging:config.disableMessaging===true,loadMask:Ext.apply({msg:"Loading..",msgCls:"x-mask-loading",maskEl:el._maskEl,hideOnReady:true,disabled:!config.loadMask},config.loadMask),_eventName:Ext.isIE?"onreadystatechange":"onload",_windowContext:null,eventsFollowFrameLinks:typeof config.eventsFollowFrameLinks=="undefined"?true:config.eventsFollowFrameLinks});el.dom[el._eventName]=el.loadHandler.createDelegate(el);var um=el.updateManager=new Ext.UpdateManager(el,true);um.showLoadIndicator=config.showLoadIndicator||false;if(config.src){el.setSrc(config.src);}else{var content=config.html||config.content||false;if(content){el.update.defer(10,el,[content]);}}return Ext.ux.ManagedIFrame.Manager.register(el);};var MIM=Ext.ux.ManagedIFrame.Manager=function(){var frames={};var readyHandler=function(e,target){try{var id=target?target.mifId:null,frame;if((frame=this.getFrameById(id||target.id))&&frame._frameAction){frame.loadHandler({type:"domready"});}}catch(rhEx){}};var implementation={shimCls:"x-frame-shim",register:function(frame){frame.manager=this;frames[frame.id]=frames[frame.dom.name]={ref:frame,elCache:{}};return frame;},deRegister:function(frame){frame._unHook();delete frames[frame.id];delete frames[frame.dom.name];},hideShims:function(){if(!this.shimApplied){return ;}Ext.select("."+this.shimCls,true).removeClass(this.shimCls+"-on");this.shimApplied=false;},showShims:function(){if(!this.shimApplied){this.shimApplied=true;Ext.select("."+this.shimCls,true).addClass(this.shimCls+"-on");}},getFrameById:function(id){return typeof id=="string"?(frames[id]?frames[id].ref||null:null):null;},getFrameByName:function(name){return this.getFrameById(name);},getFrameHash:function(frame){return frame.id?frames[frame.id]:null;},eventProxy:function(e){if(!e){return ;}e=Ext.EventObject.setEvent(e);var be=e.browserEvent||e;if(e.type=="unload"){this._unHook();}if(!be["eventPhase"]||(be["eventPhase"]==(be["AT_TARGET"]||2))){return this.fireEvent(e.type,e);}},_flyweights:{},destroy:function(){if(this._domreadySignature){Ext.EventManager.un.apply(Ext.EventManager,this._domreadySignature);}},removeNode:Ext.isIE?function(frame,n){frame=MIM.getFrameHash(frame);if(frame&&n&&n.tagName!="BODY"){d=frame.scratchDiv||(frame.scratchDiv=frame.getDocument().createElement("div"));d.appendChild(n);d.innerHTML="";}}:function(frame,n){if(n&&n.parentNode&&n.tagName!="BODY"){n.parentNode.removeChild(n);}}};if(document.addEventListener){Ext.EventManager.on.apply(Ext.EventManager,implementation._domreadySignature=[window,"DOMFrameContentLoaded",readyHandler,implementation]);}Ext.EventManager.on(window,"beforeunload",implementation.destroy,implementation);return implementation;}();MIM.showDragMask=MIM.showShims;MIM.hideDragMask=MIM.hideShims;MIM.El=function(frame,el,forceNew){var frameObj;frame=(frameObj=MIM.getFrameHash(frame))?frameObj.ref:null;if(!frame){return null;}var elCache=frameObj.elCache||(frameObj.elCache={});var dom=frame.getDom(el);if(!dom){return null;}var id=dom.id;if(forceNew!==true&&id&&elCache[id]){return elCache[id];}this.dom=dom;this.id=id||Ext.id(dom);};MIM.El.get=function(frame,el){var ex,elm,id,doc;if(!frame||!el){return null;}var frameObj;frame=(frameObj=MIM.getFrameHash(frame))?frameObj.ref:null;if(!frame){return null;}var elCache=frameObj.elCache||(frameObj.elCache={});if(!(doc=frame.getDocument())){return null;}if(typeof el=="string"){if(!(elm=frame.getDom(el))){return null;}if(ex=elCache[el]){ex.dom=elm;}else{ex=elCache[el]=new MIM.El(frame,elm);}return ex;}else{if(el.tagName){if(!(id=el.id)){id=Ext.id(el);}if(ex=elCache[id]){ex.dom=el;}else{ex=elCache[id]=new MIM.El(frame,el);}return ex;}else{if(el instanceof MIM.El){if(el!=frameObj.docEl){el.dom=frame.getDom(el.id)||el.dom;elCache[el.id]=el;}return el;}else{if(el.isComposite){return el;}else{if(Ext.isArray(el)){return frame.select(el);}else{if(el==doc){if(!frameObj.docEl){var f=function(){};f.prototype=MIM.El.prototype;frameObj.docEl=new f();frameObj.docEl.dom=doc;}return frameObj.docEl;}}}}}}return null;};Ext.apply(MIM.El.prototype,Ext.Element.prototype);Ext.extend(Ext.ux.ManagedIFrame,Ext.util.Observable,{src:null,resetUrl:Ext.isIE&&Ext.isSecure?Ext.SSL_SECURE_URL:"about:blank",setSrc:function(url,discardUrl,callback){var src=url||this.src||this.resetUrl;this._windowContext=null;this._unHook();this._frameAction=this.frameInit=this._domReady=false;if(Ext.isOpera){this.reset();}this._callBack=callback||false;this.showMask();(function(){var s=typeof src=="function"?src()||"":src;try{this._frameAction=true;this.dom.src=s;this.frameInit=true;this.checkDOM();}catch(ex){this.fireEvent("exception",this,ex);}}).defer(100,this);if(discardUrl!==true){this.src=src;}return this;},reset:function(src,callback){this.dom.src=src||this.resetUrl;if(typeof callback=="function"){callback.defer(100);}return this;},scriptRE:/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/gi,update:function(content,loadScripts,callback){loadScripts=loadScripts||this.getUpdateManager().loadScripts||false;content=Ext.DomHelper.markup(content||"");content=loadScripts===true?content:content.replace(this.scriptRE,"");var doc;if(doc=this.getDocument()){this._frameAction=!!content.length;this._windowContext=this.src=null;this._callBack=callback||false;this._unHook();this.showMask();doc.open();doc.write(content);doc.close();this.frameInit=true;if(this._frameAction){this.checkDOM();}else{this.hideMask(true);if(this._callBack){this._callBack();}}}else{this.hideMask(true);if(this._callBack){this._callBack();}}return this;},disableMessaging:true,_XFrameMessaging:function(){var tagStack={"$":[]};var isEmpty=function(v,allowBlank){return v===null||v===undefined||(!allowBlank?v==="":false);};window.sendMessage=function(message,tag,origin){var MIF;if(MIF=arguments.callee.manager){if(message._fromHost){var fn,result;var compTag=message.tag||tag||null;var mstack=!isEmpty(compTag)?tagStack[compTag.toLowerCase()]||[]:tagStack["$"];for(var i=0,l=mstack.length;i<l;i++){if(fn=mstack[i]){result=fn.apply(fn.__scope,arguments)===false?false:result;if(fn.__single){mstack[i]=null;}if(result===false){break;}}}return result;}else{message={type:isEmpty(tag)?"message":"message:"+tag.toLowerCase().replace(/^\s+|\s+$/g,""),data:message,domain:origin||document.domain,uri:document.documentURI,source:window,tag:isEmpty(tag)?null:tag.toLowerCase()};try{return MIF.disableMessaging!==true?MIF.fireEvent.call(MIF,message.type,MIF,message):null;}catch(ex){}return null;}}};window.onhostmessage=function(fn,scope,single,tag){if(typeof fn=="function"){if(!isEmpty(fn.__index)){throw"onhostmessage: duplicate handler definition"+(tag?" for tag:"+tag:"");}var k=isEmpty(tag)?"$":tag.toLowerCase();tagStack[k]||(tagStack[k]=[]);Ext.apply(fn,{__tag:k,__single:single||false,__scope:scope||window,__index:tagStack[k].length});tagStack[k].push(fn);}else{throw"onhostmessage: function required";}};window.unhostmessage=function(fn){if(typeof fn=="function"&&typeof fn.__index!="undefined"){var k=fn.__tag||"$";tagStack[k][fn.__index]=null;}};},get:function(el){return MIM.El.get(this,el);},fly:function(el,named){named=named||"_global";el=this.getDom(el);if(!el){return null;}if(!MIM._flyweights[named]){MIM._flyweights[named]=new Ext.Element.Flyweight();}MIM._flyweights[named].dom=el;return MIM._flyweights[named];},getDom:function(el){var d;if(!el||!(d=this.getDocument())){return null;}return el.dom?el.dom:(typeof el=="string"?d.getElementById(el):el);},select:function(selector,unique){var d;return(d=this.getDocument())?Ext.Element.select(selector,unique,d):null;},query:function(selector){var d;return(d=this.getDocument())?Ext.DomQuery.select(selector,d):null;},getDoc:function(){return this.get(this.getDocument());},removeNode:function(node){MIM.removeNode(this,this.getDom(node));},_unHook:function(){var elcache,h=MIM.getFrameHash(this)||{};if(this._hooked&&h&&(elcache=h.elCache)){for(var id in elcache){var el=elcache[id];delete elcache[id];if(el.removeAllListeners){el.removeAllListeners();}}if(h.docEl){h.docEl.removeAllListeners();h.docEl=null;delete h.docEl;}}this._hooked=this._domReady=this._domFired=false;},_renderHook:function(){this._windowContext=this.CSS=null;this._hooked=false;try{if(this.writeScript('(function(){(window.hostMIF = parent.Ext.get("'+this.dom.id+'"))._windowContext='+(Ext.isIE?"window":"{eval:function(s){return eval(s);}}")+";})();")){this._frameProxy||(this._frameProxy=MIM.eventProxy.createDelegate(this));var w=this.getWindow();EV.doAdd(w,"focus",this._frameProxy);EV.doAdd(w,"blur",this._frameProxy);EV.doAdd(w,"unload",this._frameProxy);if(this.disableMessaging!==true){this.loadFunction({name:"XMessage",fn:this._XFrameMessaging},false,true);var sm;if(sm=w.sendMessage){sm.manager=this;}}this.CSS=new CSSInterface(this.getDocument());}}catch(ex){}return this.domWritable();},sendMessage:function(message,tag,origin){var win;if(this.disableMessaging!==true&&(win=this.getWindow())){tag||(tag=message.tag||"");tag=tag.toLowerCase();message=Ext.applyIf(message.data?message:{data:message},{type:Ext.isEmpty(tag)?"message":"message:"+tag,domain:origin||document.domain,uri:document.documentURI,source:window,tag:tag||null,_fromHost:this});return win.sendMessage?win.sendMessage.call(null,message,tag,origin):null;}return null;},_windowContext:null,getDocument:function(){var win=this.getWindow(),doc=null;try{doc=(Ext.isIE&&win?win.document:null)||this.dom.contentDocument||window.frames[this.id].document||null;}catch(gdEx){return false;}return doc;},getBody:function(){var d;return(d=this.getDocument())?d.body:null;},getDocumentURI:function(){var URI,d;try{URI=this.src&&(d=this.getDocument())?d.location.href:null;}catch(ex){}return URI||this.src;},getWindow:function(){var dom=this.dom,win=null;try{win=dom.contentWindow||window.frames[dom.name]||null;}catch(gwEx){}return win;},print:function(){try{var win=this.getWindow();if(Ext.isIE){win.focus();}win.print();}catch(ex){throw"print exception: "+(ex.description||ex.message||ex);}},destroy:function(){this.removeAllListeners();if(this.dom){this.dom[this._eventName]=null;Ext.ux.ManagedIFrame.Manager.deRegister(this);this._windowContext=null;if(Ext.isIE&&this.dom.src){this.dom.src="javascript:false";}this._maskEl=null;this.remove();}if(this.loadMask){Ext.apply(this.loadMask,{masker:null,maskEl:null});}},domWritable:function(){return !!this._windowContext;},execScript:function(block,useDOM){try{if(this.domWritable()){if(useDOM){this.writeScript(block);}else{return this._windowContext.eval(block);}}else{throw"execScript:non-secure context";}}catch(ex){this.fireEvent("exception",this,ex);return false;}return true;},writeScript:function(block,attributes){attributes=Ext.apply({},attributes||{},{type:"text/javascript",text:block});try{var head,script,doc=this.getDocument();if(doc&&typeof doc.getElementsByTagName!="undefined"){if(!(head=doc.getElementsByTagName("head")[0])){head=doc.createElement("head");doc.getElementsByTagName("html")[0].appendChild(head);}if(head&&(script=doc.createElement("script"))){for(var attrib in attributes){if(attributes.hasOwnProperty(attrib)&&attrib in script){script[attrib]=attributes[attrib];}}return !!head.appendChild(script);}}}catch(ex){this.fireEvent("exception",this,ex);}return false;},loadFunction:function(fn,useDOM,invokeIt){var name=fn.name||fn;var fn=fn.fn||window[fn];this.execScript(name+"="+fn,useDOM);if(invokeIt){this.execScript(name+"()");}},showMask:function(msg,msgCls,forced){var lmask;if((lmask=this.loadMask)&&(!lmask.disabled||forced)){if(lmask._vis){return ;}lmask.masker||(lmask.masker=Ext.get(lmask.maskEl||this.dom.parentNode||this.wrap({tag:"div",style:{position:"relative"}})));lmask._vis=true;lmask.masker.mask.defer(lmask.delay||5,lmask.masker,[msg||lmask.msg,msgCls||lmask.msgCls]);}},hideMask:function(forced){var tlm;if((tlm=this.loadMask)&&!tlm.disabled&&tlm.masker){if(!forced&&(tlm.hideOnReady!==true&&this._domReady)){return ;}tlm._vis=false;tlm.masker.unmask.defer(tlm.delay||5,tlm.masker);}},loadHandler:function(e,target){if(!this.frameInit||(!this._frameAction&&!this.eventsFollowFrameLinks)){return ;}target||(target={});var rstatus=(e&&typeof e.type!=="undefined"?e.type:this.dom.readyState);switch(rstatus){case"loading":case"interactive":break;case"domready":if(this._domReady){return ;}this._domReady=true;if(this._hooked=this._renderHook()){this._domFired=true;this.fireEvent("domready",this);}case"domfail":this._domReady=true;this.hideMask();break;case"load":case"complete":if(!this._domReady){this.loadHandler({type:"domready",id:this.id});}this.hideMask(true);if(this._frameAction||this.eventsFollowFrameLinks){this.fireEvent.defer(50,this,["documentloaded",this]);}this._frameAction=this._frameInit=false;if(this.eventsFollowFrameLinks){this._domFired=this._domReady=false;}if(this._callBack){this._callBack(this);}break;default:}this.frameState=rstatus;},checkDOM:function(win){if(Ext.isOpera||Ext.isGecko||!this._frameAction){return ;}var n=0,win=win||this.getWindow(),manager=this,domReady=false,max=300;var poll=function(){try{var doc=manager.getDocument(),body=null;if(doc===false){throw"Document Access Denied";}if(!manager._domReady){domReady=!!(doc&&doc.getElementsByTagName);domReady=domReady&&(body=doc.getElementsByTagName("body")[0])&&!!body.innerHTML.length;}}catch(ex){n=max;}if(!manager._frameAction||manager._domReady){return ;}if((++n<max)&&!domReady){setTimeout(arguments.callee,10);return ;}manager.loadHandler({type:domReady?"domready":"domfail"});};setTimeout(poll,40);}});var styleCamelRe=/(-[a-z])/gi;var styleCamelFn=function(m,a){return a.charAt(1).toUpperCase();};var CSSInterface=function(hostDocument){var doc;if(hostDocument){doc=hostDocument;return{rules:null,createStyleSheet:function(cssText,id){var ss;if(!doc){return ;}var head=doc.getElementsByTagName("head")[0];var rules=doc.createElement("style");rules.setAttribute("type","text/css");if(id){rules.setAttribute("id",id);}if(Ext.isIE){head.appendChild(rules);ss=rules.styleSheet;ss.cssText=cssText;}else{try{rules.appendChild(doc.createTextNode(cssText));}catch(e){rules.cssText=cssText;}head.appendChild(rules);ss=rules.styleSheet?rules.styleSheet:(rules.sheet||doc.styleSheets[doc.styleSheets.length-1]);}this.cacheStyleSheet(ss);return ss;},removeStyleSheet:function(id){if(!doc){return ;}var existing=doc.getElementById(id);if(existing){existing.parentNode.removeChild(existing);}},swapStyleSheet:function(id,url){this.removeStyleSheet(id);if(!doc){return ;}var ss=doc.createElement("link");ss.setAttribute("rel","stylesheet");ss.setAttribute("type","text/css");ss.setAttribute("id",id);ss.setAttribute("href",url);doc.getElementsByTagName("head")[0].appendChild(ss);},refreshCache:function(){return this.getRules(true);},cacheStyleSheet:function(ss){if(this.rules){this.rules={};}try{var ssRules=ss.cssRules||ss.rules;for(var j=ssRules.length-1;j>=0;--j){this.rules[ssRules[j].selectorText]=ssRules[j];}}catch(e){}},getRules:function(refreshCache){if(this.rules==null||refreshCache){this.rules={};if(doc){var ds=doc.styleSheets;for(var i=0,len=ds.length;i<len;i++){try{this.cacheStyleSheet(ds[i]);}catch(e){}}}}return this.rules;},getRule:function(selector,refreshCache){var rs=this.getRules(refreshCache);if(!Ext.isArray(selector)){return rs[selector];}for(var i=0;i<selector.length;i++){if(rs[selector[i]]){return rs[selector[i]];}}return null;},updateRule:function(selector,property,value){if(!Ext.isArray(selector)){var rule=this.getRule(selector);if(rule){rule.style[property.replace(styleCamelRe,styleCamelFn)]=value;return true;}}else{for(var i=0;i<selector.length;i++){if(this.updateRule(selector[i],property,value)){return true;}}}return false;}};}};Ext.ux.ManagedIframePanel=Ext.extend(Ext.Panel,{defaultSrc:null,bodyStyle:{height:"100%",width:"100%",position:"relative"},frameStyle:{overflow:"auto"},frameConfig:null,hideMode:!Ext.isIE?"nosize":"display",shimCls:Ext.ux.ManagedIFrame.Manager.shimCls,shimUrl:null,loadMask:false,stateful:false,animCollapse:Ext.isIE&&Ext.enableFx,autoScroll:false,closable:true,ctype:"Ext.ux.ManagedIframePanel",showLoadIndicator:false,unsupportedText:"Inline frames are NOT enabled/supported by your browser.",initComponent:function(){this.bodyCfg||(this.bodyCfg={cls:"x-managed-iframe-mask",children:[Ext.apply({tag:"iframe",frameborder:0,cls:"x-managed-iframe",style:this.frameStyle||null,html:this.unsupportedText||null},this.frameConfig?this.frameConfig.autoCreate||{}:false,Ext.isIE&&Ext.isSecure?{src:Ext.SSL_SECURE_URL}:false),{tag:"img",src:this.shimUrl||Ext.BLANK_IMAGE_URL,cls:this.shimCls,galleryimg:"no"}]});this.autoScroll=false;this.items=null;if(this.stateful!==false){this.stateEvents||(this.stateEvents=["documentloaded"]);}Ext.ux.ManagedIframePanel.superclass.initComponent.call(this);this.monitorResize||(this.monitorResize=this.fitToParent);this.addEvents({documentloaded:true,domready:true,message:true,exception:true});this.addListener=this.on;},doLayout:function(){if(this.fitToParent&&!this.ownerCt){var pos=this.getPosition(),size=(Ext.get(this.fitToParent)||this.getEl().parent()).getViewSize();this.setSize(size.width-pos[0],size.height-pos[1]);}Ext.ux.ManagedIframePanel.superclass.doLayout.apply(this,arguments);},beforeDestroy:function(){if(this.rendered){if(this.tools){for(var k in this.tools){Ext.destroy(this.tools[k]);}}if(this.header&&this.headerAsText){var s;if(s=this.header.child("span")){s.remove();}this.header.update("");}Ext.each(["iframe","shim","header","topToolbar","bottomToolbar","footer","loadMask","body","bwrap"],function(elName){if(this[elName]){if(typeof this[elName].destroy=="function"){this[elName].destroy();}else{Ext.destroy(this[elName]);}this[elName]=null;delete this[elName];}},this);}Ext.ux.ManagedIframePanel.superclass.beforeDestroy.call(this);},onDestroy:function(){Ext.Panel.superclass.onDestroy.call(this);},onRender:function(ct,position){Ext.ux.ManagedIframePanel.superclass.onRender.call(this,ct,position);if(this.iframe=this.body.child("iframe.x-managed-iframe")){this.iframe.ownerCt=this;var El=Ext.Element;var mode=El[this.hideMode.toUpperCase()]||"x-hide-nosize";Ext.each([this[this.collapseEl],this.floating?null:this.getActionEl(),this.iframe],function(el){if(el){el.setVisibilityMode(mode);}},this);if(this.loadMask){this.loadMask=Ext.apply({disabled:false,maskEl:this.body,hideOnReady:true},this.loadMask);}if(this.iframe=new Ext.ux.ManagedIFrame(this.iframe,{loadMask:this.loadMask,showLoadIndicator:this.showLoadIndicator,disableMessaging:this.disableMessaging,style:this.frameStyle})){this.loadMask=this.iframe.loadMask;this.relayEvents(this.iframe,["blur","focus","unload","documentloaded","domready","exception","message"].concat(this._msgTagHandlers||[]));delete this._msgTagHandlers;}this.getUpdater().showLoadIndicator=this.showLoadIndicator||false;var ownerCt=this.ownerCt;while(ownerCt){ownerCt.on("afterlayout",function(container,layout){var MIM=Ext.ux.ManagedIFrame.Manager,st=false;Ext.each(["north","south","east","west"],function(region){var reg;if((reg=layout[region])&&reg.splitEl){st=true;if(!reg.split._splitTrapped){reg.split.on("beforeresize",MIM.showShims,MIM);reg.split._splitTrapped=true;}}},this);if(st&&!this._splitTrapped){this.on("resize",MIM.hideShims,MIM);this._splitTrapped=true;}},this,{single:true});ownerCt=ownerCt.ownerCt;}}this.shim=Ext.get(this.body.child("."+this.shimCls));},toggleShim:function(){if(this.shim&&this.shimCls){this.shim.toggleClass(this.shimCls+"-on");}},afterRender:function(container){var html=this.html;delete this.html;Ext.ux.ManagedIframePanel.superclass.afterRender.call(this);if(this.iframe){if(this.defaultSrc){this.setSrc();}else{if(html){this.iframe.update(typeof html=="object"?Ext.DomHelper.markup(html):html);}}}},sendMessage:function(){if(this.iframe){this.iframe.sendMessage.apply(this.iframe,arguments);}},on:function(name){var tagRE=/^message\:/i,n=null;if(typeof name=="object"){for(var na in name){if(!this.filterOptRe.test(na)&&tagRE.test(na)){n||(n=[]);n.push(na.toLowerCase());}}}else{if(tagRE.test(name)){n=[name.toLowerCase()];}}if(this.getFrame()&&n){this.relayEvents(this.iframe,n);}else{this._msgTagHandlers||(this._msgTagHandlers=[]);if(n){this._msgTagHandlers=this._msgTagHandlers.concat(n);}}Ext.ux.ManagedIframePanel.superclass.on.apply(this,arguments);},setSrc:function(url,discardUrl,callback){url=url||this.defaultSrc||false;if(!url){return this;}if(url.url){callback=url.callback||false;discardUrl=url.discardUrl||false;url=url.url||false;}var src=url||(Ext.isIE&&Ext.isSecure?Ext.SSL_SECURE_URL:"");if(this.rendered&&this.iframe){this.iframe.setSrc(src,discardUrl,callback);}return this;},getState:function(){var URI=this.iframe?this.iframe.getDocumentURI()||null:null;return Ext.apply(Ext.ux.ManagedIframePanel.superclass.getState.call(this)||{},URI?{defaultSrc:typeof URI=="function"?URI():URI}:null);},getUpdater:function(){return this.rendered?(this.iframe||this.body).getUpdater():null;},getFrame:function(){return this.rendered?this.iframe:null;},getFrameWindow:function(){return this.rendered&&this.iframe?this.iframe.getWindow():null;},getFrameDocument:function(){return this.rendered&&this.iframe?this.iframe.getDocument():null;},getFrameDoc:function(){return this.rendered&&this.iframe?this.iframe.getDoc():null;},getFrameBody:function(){return this.rendered&&this.iframe?this.iframe.getBody():null;},load:function(loadCfg){var um;if(um=this.getUpdater()){if(loadCfg&&loadCfg.renderer){um.setRenderer(loadCfg.renderer);delete loadCfg.renderer;}um.update.apply(um,arguments);}return this;},doAutoLoad:function(){this.load(typeof this.autoLoad=="object"?this.autoLoad:{url:this.autoLoad});}});Ext.reg("iframepanel",Ext.ux.ManagedIframePanel);Ext.ux.ManagedIframePortlet=Ext.extend(Ext.ux.ManagedIframePanel,{anchor:"100%",frame:true,collapseEl:"bwrap",collapsible:true,draggable:true,cls:"x-portlet"});Ext.reg("iframeportlet",Ext.ux.ManagedIframePortlet);Ext.apply(Ext.Element.prototype,{setVisible:function(visible,animate){if(!animate||!Ext.lib.Anim){if(this.visibilityMode==Ext.Element.DISPLAY){this.setDisplayed(visible);}else{if(this.visibilityMode==Ext.Element.VISIBILITY){this.fixDisplay();this.dom.style.visibility=visible?"visible":"hidden";}else{this[visible?"removeClass":"addClass"](String(this.visibilityMode));}}}else{var dom=this.dom;var visMode=this.visibilityMode;if(visible){this.setOpacity(0.01);this.setVisible(true);}this.anim({opacity:{to:(visible?1:0)}},this.preanim(arguments,1),null,0.35,"easeIn",function(){if(!visible){if(visMode==Ext.Element.DISPLAY){dom.style.display="none";}else{if(visMode==Ext.Element.VISIBILITY){dom.style.visibility="hidden";}else{Ext.get(dom).addClass(String(visMode));}}Ext.get(dom).setOpacity(1);}});}return this;},isVisible:function(deep){var vis=!(this.getStyle("visibility")=="hidden"||this.getStyle("display")=="none"||this.hasClass(this.visibilityMode));if(deep!==true||!vis){return vis;}var p=this.dom.parentNode;while(p&&p.tagName.toLowerCase()!="body"){if(!Ext.fly(p,"_isVisible").isVisible()){return false;}p=p.parentNode;}return true;}});Ext.onReady(function(){var CSS=Ext.util.CSS,rules=[];CSS.getRule(".x-managed-iframe")||(rules.push(".x-managed-iframe {height:100%;width:100%;overflow:auto;}"));CSS.getRule(".x-managed-iframe-mask")||(rules.push(".x-managed-iframe-mask{width:100%;height:100%;position:relative;}"));if(!CSS.getRule(".x-frame-shim")){rules.push(".x-frame-shim {z-index:8500;position:absolute;top:0px;left:0px;background:transparent!important;overflow:hidden;display:none;}");rules.push(".x-frame-shim-on{width:100%;height:100%;display:block;zoom:1;}");rules.push(".ext-ie6 .x-frame-shim{margin-left:5px;margin-top:3px;}");}CSS.getRule(".x-hide-nosize")||(rules.push(".x-hide-nosize,.x-hide-nosize *{height:0px!important;width:0px!important;border:none;}"));if(!!rules.length){CSS.createStyleSheet(rules.join(" "));}});})();;(function(){var A=Ext.ux.Media=function(C){Ext.apply(this,C||{});this.toString=this.mediaMarkup;this.initMedia();};A.mediaTypes={"PDF":Ext.apply({tag:"object",cls:"x-media x-media-pdf",type:"application/pdf",style:{position:"absolute",top:"0px",left:"0px","z-index":11002},data:"@url",autoSize:true,params:{src:"@url"}},Ext.isIE?{classid:"CLSID:CA8A9780-280D-11CF-A24D-444553540000"}:false),"WMV":Ext.apply({tag:"object",cls:"x-media x-media-wmv",type:"video/x-ms-wmv",data:"@url",params:{src:"@url",url:"@url",autostart:"@start",showcontrols:"@controls"}},Ext.isIE?{classid:"CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6"}:false),"SWF":Ext.apply({tag:"object",cls:"x-media x-media-swf",type:"application/x-shockwave-flash",data:"@url",scripting:"sameDomain",loop:true,start:false,unsupportedText:{cn:["The Adobe Flash Player{0}is required.",{tag:"br"},{tag:"a",cn:"The free download is available here.",href:"http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash",target:"_flash"}]},params:{movie:"@url",menu:"@controls",play:"@start",quality:"high",allowscriptaccess:"@scripting",bgcolor:"#FFFFFF",wmode:"opaque",loop:"@loop"}},Ext.isIE?{classid:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",codebase:"http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"}:false),"JWP":Ext.apply({tag:"object",cls:"x-media x-media-swf x-media-flv",type:"application/x-shockwave-flash",data:"@url",loop:false,start:false,params:{movie:"@url",flashVars:{autostart:"@start",repeat:"@loop",height:"@height",width:"@width",id:"@id"}}},Ext.isIE?{classid:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"}:false),"MOV":Ext.apply({tag:"object",cls:"x-media x-media-mov",type:"video/quicktime",data:"@url",params:{src:"@url",autoplay:"@start",controller:"@controls",enablejavascript:"@scripting"}},Ext.isIE?{classid:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",codebase:"http://www.apple.com/qtactivex/qtplugin.cab",type:"application/x-oleobject",params:{src:"@url",controller:"@controls",autostart:"@start"}}:false),"REAL":Ext.apply({tag:"object",cls:"x-media x-media-real",type:"audio/x-pn-realaudio",data:"@url",standby:"Loading Real Media Player components...",params:{src:"@url",autostart:"@start",controller:"@controls",controlls:"@controls",loop:"@loop",console:"TES"}},Ext.isIE?{classid:"clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA"}:false),"SVG":{tag:"object",cls:"x-media x-media-img x-media-svg",type:"image/svg+xml",data:"@url"},"GIF":{tag:"img",cls:"x-media x-media-img x-media-gif",src:"@url"},"JPEG":{tag:"img",cls:"x-media x-media-img x-media-jpeg",src:"@url"},"JP2":{tag:"object",cls:"x-media x-media-img x-media-jp2",type:"image/jpeg2000-image",data:"@url"},"PNG":{tag:"img",cls:"x-media x-media-img x-media-png",src:"@url"},"HTM":{tag:"iframe",cls:"x-media x-media-html",frameBorder:0,style:{overflow:"auto"},src:"@url"},"TXT":{tag:"object",cls:"x-media x-media-text",type:"text/plain",style:{overflow:"auto"},data:"@url"},"SILVER":{tag:"object",cls:"x-media x-media-silverlight",type:"application/x-silverlight",data:"@url"},"OFFICE":{tag:"object",cls:"x-media x-media-office",type:"application/x-msoffice",data:"@url"},"XML":{tag:"iframe",cls:"x-media x-media-xml",style:{overflow:"auto"},src:"@url"},"QTMIDI":Ext.apply({tag:"object",cls:"x-media x-media-audio x-media-midi",type:"audio/midi",style:{overflow:"auto"},data:"@url",params:{autostart:"@start"}},Ext.isIE?{classid:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",codebase:"http://www.apple.com/qtactivex/qtplugin.cab",type:"application/x-oleobject",params:{src:"@url",controller:"@controls",autostart:"@start",enablejavascript:"@scripting"}}:false),"QTMP3":Ext.apply({tag:"object",cls:"x-media x-media-audio x-media-mpeg",type:"audio/mpeg",data:"@url",params:{autostart:"@start"}},Ext.isIE?{classid:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",codebase:"http://www.apple.com/qtactivex/qtplugin.cab",type:"application/x-oleobject",params:{src:"@url",controller:"@controls",autostart:"@start",enablejavascript:"@scripting"}}:false),"QTWAV":Ext.apply({tag:"object",cls:"x-media x-media-audio x-media-wav",type:"audio/wav",data:"@url",params:{autostart:"@start"}},Ext.isIE?{classid:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",codebase:"http://www.apple.com/qtactivex/qtplugin.cab",type:"application/x-oleobject",params:{src:"@url",controller:"@controls",autostart:"@start",enablejavascript:"@scripting"}}:false)};Ext.apply(A.prototype,{mediaObject:null,mediaCfg:null,mediaVersion:null,requiredVersion:null,unsupportedText:null,init:function(C){if(C&&this.getEl==undefined){Ext.applyIf(C,this);}},initMedia:function(){},onBeforeMedia:function(G,E,F){var C=G||this.mediaCfg,D;if(C&&(D=this.getMediaType(C.mediaType))){C.autoSize=(C.autoSize==undefined?D.autoSize===true:C.autoSize===true);if(C.autoSize&&(E=Ext.isReady?Ext.get(E||this.lastCt):null)){C.height=E.getHeight(true)||this.assert(C.height,"100%");C.width=E.getWidth(true)||this.assert(C.width,"100%");}}},onAfterMedia:function(){var C=this.mediaCfg?this.mediaCfg.listeners:null;if(C&&this.mediaObject){this.mediaObject.on(C);}},getMediaType:function(C){return A.mediaTypes[C];},assert:function(C,D){C=typeof C=="function"?C.call(C.scope||null):C;return Ext.value(C,D);},mediaMarkup:function(C,E,O,J){C=C||this.mediaCfg;if(!C){return"";}var G=Ext.apply({url:false,autoSize:false},C);G.url=this.assert(G.url,false);if(G.url){var M,D,H=Ext.Element;var F=Ext.apply({},this.getMediaType(G.mediaType)||false);var I=Ext.apply(F.params||{},G.params||{});for(var N in I){if(I.hasOwnProperty(N)){G.children||(G.children=[]);D=this.assert(I[N],null);if(D!==null){G.children.push({tag:"param",name:N,value:typeof D=="object"?Ext.urlEncode(D):encodeURI(D)});}}}delete F.params;F.style=this.assert(F.style,{});G.height=this.assert(O||G.height||F.height,"100%");G.width=this.assert(E||G.width||F.width,"100%");Ext.apply(F.style,{height:H.addUnits((G.autoSize?"100%":G.height),H.prototype.defaultUnit),width:H.addUnits((G.autoSize?"100%":G.width),H.prototype.defaultUnit)});F.html=this.assert(G.unsupportedText||this.unsupportedText||F.unsupportedText,null);G=Ext.apply({tag:"object"},G,F);G.id||(G.id=Ext.id());var L={url:G.url||"",height:parseInt(G.height,10)||100,width:parseInt(G.width,10)||100,scripting:this.assert(G.scripting,false),controls:this.assert(G.controls,false),start:this.assert(G.start,false),loop:this.assert(G.loop,false),volume:this.assert(G.volume,10),id:G.id};delete G.url;delete G.mediaType;delete G.controls;delete G.start;delete G.loop;delete G.scripting;delete G.volume;delete G.autoSize;delete G.params;delete G.unsupportedText;delete G.renderOnResize;delete G.listeners;delete G.height;delete G.width;return Ext.DomHelper.markup(G).replace(/(%40url|@url)/g,L.url).replace(/(%40start|@start)/g,L.start+"").replace(/(%40controls|@controls)/g,L.controls+"").replace(/(%40id|@id)/g,L.id+"").replace(/(%40loop|@loop)/g,L.loop+"").replace(/(%40volume|@volume)/g,L.volume+"").replace(/(%40scripting|@scripting)/g,L.scripting+"").replace(/(%40width|@width)/g,L.width+"").replace(/(%40height|@height)/g,L.height+"");}else{var K=this.assert(G.unsupportedText||this.unsupportedText||F.unsupportedText,null);K=K?Ext.DomHelper.markup(K):null;return String.format(K||"Media Configuration/Plugin Error"," "," ");}},renderMedia:function(I,E,H,C,F){if(!Ext.isReady){Ext.onReady(this.renderMedia.createDelegate(this,Array.prototype.slice.call(arguments,0)));}var G=this.mediaCfg=I||this.mediaCfg;E=Ext.get(E||this.lastCt||(this.mediaObject?this.mediaObject.parentNode:null));this.onBeforeMedia.call(this,G,E,H,C,F);if(E){var D;if(E&&G&&(D=this.mediaMarkup(G,C,F,E))){this.clearMedia();this.mediaObject=Ext.get(Ext.DomHelper.insertHtml(H||"afterbegin",E.dom,D));}}this.onAfterMedia(E);this.lastCt=E;},clearMedia:function(){if(Ext.isReady){Ext.destroy(this.mediaObject);}this.mediaObject=null;},getInterface:function(){return this.mediaObject?this.mediaObject.dom:null;},detectVersion:Ext.emptyFn});var B=function(){};Ext.apply(B.prototype,{hideMode:!Ext.isIE?"nosize":"display",animCollapse:false,visibilityCls:!Ext.isIE?"x-hide-nosize":null,autoScroll:true,shadow:false,bodyStyle:{position:"relative"},resizeMedia:function(D,C,E){var F=this.mediaCfg;if(F&&this.boxReady){if(arguments.length>3&&(!this.mediaObject||F.renderOnResize)){this.refreshMedia(this.body||this.el);}}},adjustVisibility:function(C){C=[].concat(C||[]);var D=Ext.Element;var E=this.visibilityCls||D[this.hideMode.toUpperCase()]||D.VISIBILITY;Ext.each(C,function(F){if(F){F.setVisibilityMode(E);}});if(this.loadMask){(this.loadMask=new Ext.LoadMask(this.bwrap||this.getEl(),Ext.apply({},this.loadMask))).removeMask=false;}this.on("resize",this.resizeMedia,this);},doAutoLoad:Ext.emptyFn,refreshMedia:function(C){if(this.mediaCfg){this.renderMedia(null,C);}},loadMask:false});Ext.ux.MediaComponent=Ext.extend(Ext.BoxComponent,{ctype:"Ext.ux.MediaComponent",autoEl:"div",cls:"x-media-comp",getId:function(){return this.id||(this.id="media-comp"+(++Ext.Component.AUTO_ID));},initComponent:function(){this.initMedia();Ext.ux.MediaComponent.superclass.initComponent.apply(this,arguments);},afterRender:function(){this.setAutoScroll();Ext.ux.MediaComponent.superclass.afterRender.apply(this,arguments);this.adjustVisibility(this.getEl());},beforeDestroy:function(){this.clearMedia();Ext.ux.MediaComponent.superclass.beforeDestroy.call(this);},setAutoScroll:function(){if(this.rendered&&this.autoScroll){this.getEl().setOverflow("auto");}}});Ext.apply(Ext.ux.MediaComponent.prototype,Ext.ux.Media.prototype);Ext.apply(Ext.ux.MediaComponent.prototype,B.prototype);Ext.reg("media",Ext.ux.MediaComponent);A.Panel=Ext.extend(Ext.Panel,{ctype:"Ext.ux.Media.Panel",cls:"x-media-panel",initComponent:function(){this.initMedia();delete this.html;this.contentEl=null;this.items=null;A.Panel.superclass.initComponent.call(this);},onRender:function(){A.Panel.superclass.onRender.apply(this,arguments);this.adjustVisibility([this[this.collapseEl],this.floating?null:this.getActionEl()]);},beforeDestroy:function(){this.clearMedia();A.Panel.superclass.beforeDestroy.call(this);}});Ext.apply(A.Panel.prototype,A.prototype);Ext.apply(A.Panel.prototype,B.prototype);Ext.reg("mediapanel",Ext.ux.MediaPanel=A.Panel);A.Window=Ext.extend(Ext.Window,{cls:"x-media-window",ctype:"Ext.ux.Media.Window",initComponent:function(){this.initMedia();delete this.html;this.contentEl=null;this.items=null;A.Window.superclass.initComponent.call(this);},onRender:function(){A.Window.superclass.onRender.apply(this,arguments);this.adjustVisibility([this[this.collapseEl]],this.floating?null:this.getActionEl());},beforeDestroy:function(){this.clearMedia();A.Window.superclass.beforeDestroy.call(this);}});Ext.apply(A.Window.prototype,A.prototype);Ext.apply(A.Window.prototype,B.prototype);Ext.ux.MediaWindow=A.Window;Ext.onReady(function(){var C=Ext.util.CSS,D=[];C.getRule(".x-media")||(D.push(".x-media{width:100%;height:100%;display:block;overflow:none;}"));C.getRule(".x-media-mask")||(D.push(".x-media-mask{position:absolute;top:0px;left:0px;background-color:transparent;width:auto;height:auto;zoom:1;}"));C.getRule(".x-hide-nosize .x-media")||(D.push(".x-hide-nosize,.x-hide-nosize .x-media {height:0!important;width:0!important;border:none!important;}"));if(!!D.length){C.createStyleSheet(D.join(""));}});Ext.apply(Ext.Element.prototype,{setVisible:function(F,C){if(!C||!Ext.lib.Anim){if(this.visibilityMode==Ext.Element.DISPLAY){this.setDisplayed(F);}else{if(this.visibilityMode==Ext.Element.VISIBILITY){this.fixDisplay();this.dom.style.visibility=F?"visible":"hidden";}else{this[F?"removeClass":"addClass"](String(this.visibilityMode));}}}else{var E=this.dom;var D=this.visibilityMode;if(F){this.setOpacity(0.01);this.setVisible(true);}this.anim({opacity:{to:(F?1:0)}},this.preanim(arguments,1),null,0.35,"easeIn",function(){if(!F){if(D==Ext.Element.DISPLAY){E.style.display="none";}else{if(D==Ext.Element.VISIBILITY){E.style.visibility="hidden";}else{Ext.get(E).addClass(String(D));}}Ext.get(E).setOpacity(1);}});}return this;},isVisible:function(C){var D=!(this.getStyle("visibility")=="hidden"||this.getStyle("display")=="none"||this.hasClass(this.visibilityMode));if(C!==true||!D){return D;}var E=this.dom.parentNode;while(E&&E.tagName.toLowerCase()!="body"){if(!Ext.fly(E,"_isVisible").isVisible()){return false;}E=E.parentNode;}return true;}});})();;/**
 * Thanks: http://extjs.com/forum/showthread.php?t=44107
 * 
 * --------------------------------------------------------------
 * 消息：
 * 点击了链接
 * 
 * 消息名：     			
 * ux.clickAction   
 * 
 * 消息内容：
 * {int} sender 本组件的id值
 * {int} targetId 链接Id
 * --------------------------------------------------------------
 */
Ext.ux.ClickActionPanel = Ext.extend(Ext.Panel, {
    
    doAction: function(e, t){
        e.stopEvent();
        //this.fireEvent('clicked_on_' + t.id);
        this.publish("ux.clickAction",{
        	targetId : t.id,
        	sender:this.id
        })
    },
    
    onRender: function () {
        Ext.ux.ClickActionPanel.superclass.onRender.apply(this, arguments);
        this.body.on('mousedown', this.doAction, this, {delegate:'a'});
        this.body.on('click',Ext.emptyFn,null,{delegate:'a', preventDefault:true});
    }
    
});

Ext.reg('clickactionpanel', Ext.ux.ClickActionPanel); // register xtype
//EOP
;/**
 * 带有定制按钮和链接的面板（只用作基类）
 */
divo.panel.PanelWithHtmlBtns = Ext.extend(Ext.Panel, {
	initComponent : function() {
		divo.waitFor(this.canSetupBtns, this.setupBtns, this)
		divo.panel.PanelWithHtmlBtns.superclass.initComponent.apply(this,arguments)
	},
	getHtmlBtnsPanel : function(btns) {
		this.htmlBtns = btns

		var html = []
		html.push('<div style="padding-top:8px" id="' + this.myId('btns') + '">&nbsp;')
		var hasBtns = false
		for (btn in this.htmlBtns) {
			if (!this.htmlBtns[btn].hidden) {
				if (this.htmlBtns[btn].link) {
					html.push('<a class="common-text" id="'
								+ this.myId('btn-' + btn)	
								+ '" href="#"><img style="padding-top:10px" src="media/images/'
								+ this.htmlBtns[btn].image + '">'+this.htmlBtns[btn].link+'</a>&nbsp;&nbsp;')
				} else {
					html.push('<a id="'
								+ this.myId('btn-' + btn)
								+ '" href="#"><img src="media/images/btns/'
								+ this.htmlBtns[btn].image + '"></a>&nbsp;&nbsp;')
				}
				hasBtns = true
			}
		}
		html.push('</div>')
		return {
			region : 'north',
			layout : 'fit',
			height : hasBtns?50:0,
			split : false,
			ctCls : "panel-html-btns",
			html : html.join('')
		}
	},
	canSetupBtns : function() {
		return Ext.get(this.myId('btns'))
	},
	setupBtns : function() {
		var el = Ext.get(this.myId('btns'))
		el.on({
			click : {
				stopEvent : false,
				delegate : 'a',
				scope : this,
				fn : function(e, target) {
					var elIds = target.id.split('-')
					for (btn in this.htmlBtns) {
						if (btn == elIds[elIds.length - 1]) {
							this.htmlBtns[btn].fn.call(this)
						}
					}
				}
			}
		})
	}
})

// EOP

;/**
 * @class Ext.ux.ColumnMenu
 * @extends Ext.menu.Menu
 * A menu object.  This is the container to which you add all other menu items in column format.  Menu can also serve a as a base class
 * when you want a specialzed menu based off of another component (like {@link Ext.menu.DateMenu} for example).
 * @constructor
 * Creates a new Column Menu
 * @param {Object} config Configuration options
 */
Ext.ux.ColumnMenu = function(config) {
	Ext.apply(this,config);
    Ext.ux.ColumnMenu.superclass.constructor.call(this, config);
};

Ext.extend( Ext.ux.ColumnMenu , Ext.menu.Menu , {
    /**
     * @cfg {Number} columnHeight The max height of a menu column in pixels (defaults to 300)
     */
	columnHeight: 300,
    /**
     * @cfg {Number} columnWidth The width of a menu column in pixels (defaults to 180)
     */
	columnWidth: 180,
	
	// private
    render: function() {
        if ( this.el ) {
            return;
        }
        
        var el = this.el = this.createEl();

        if ( !this.keyNav ) {
            this.keyNav = new Ext.menu.MenuNav( this );
        }
        
        if ( this.plain ) {
            el.addClass("x-menu-plain");
        }
        
        if ( this.cls ) {
            el.addClass( this.cls );
        }

        var focusEl = this.focusEl = el.createChild({
            cls: "x-menu-focus",
            href: "#",
            onclick: "return false;",
            tabIndex:"-1",
            tag: "a"
        });
        
        el.setStyle({
			'background': '',
			'margin': '0',
        	'padding': '0'
        });
        
        var containerEl = this.containerEl = el.createChild({
            cls: "x-column-menu",
            tag: "div"
        });

		var columnEl = null;
        var ul = null;
        var li = null;

        this.items.each(function( item , index , length ) {
            if ( ul === null || ul.getHeight() >= this.columnHeight ) {
	            columnEl = containerEl.createChild({
	                cls: "x-menu-list",
	                tag: "div"
	            });
	            
	            ul = columnEl.createChild({
	                style: "width: " + this.columnWidth + "px;",
	                tag: "ul"
	            });
	            
	            ul.on("click", this.onClick, this);
	            ul.on("mouseover", this.onMouseOver, this);
	            ul.on("mouseout", this.onMouseOut, this);
	            
	            this.ul = ul;
			}
            
            li = document.createElement("li");
            li.className = "x-menu-list-item";
            
            ul.dom.appendChild( li );
            
            item.render( li , this );
		}.createDelegate( this ));
		
		containerEl.child('.x-menu-list:last').setHeight( containerEl.child('.x-menu-list:first').getComputedHeight() );
    }
});
//EOP
;//Thanks: http://extjs.com/forum/showthread.php?t=40842
Ext.namespace('Ext.ux');
//Utility class
if(!Ext.ux.Utility)
    Ext.ux.Utility = {

        isNullOrUndefined: function(obj) {
            return (typeof obj == 'undefined' || obj == null );
        },
        isFunction: function(f){
            return typeof f == 'function';
        }
        
    };

Ext.ux.FieldReadOnlyPlugin = function(){
    
    this.init = function(f){
         f.setReadOnly = function(value){
            if(f.readOnly)//if readonly is set in the Ext way, like in cfg object, do not take any action.
                return;
            f._readOnly = value;
            if(f.rendered){
                if(Ext.ux.Utility.isNullOrUndefined(f.editable) || f.editable === true){
                    var el = f.getEl();
                    el.dom.setAttribute('readOnly', value);
                    el.dom.readOnly = value;
                }
            }
            else {
                f.readOnly = value;
            }
         };
        
        if(Ext.ux.Utility.isFunction(f.expand))
            f.expand = f.expand.createInterceptor(function(){
                return !f._readOnly;      
            }); 
        if(Ext.ux.Utility.isFunction(f.onTriggerClick))
            f.onTriggerClick = f.onTriggerClick.createInterceptor(function(){
                return !f._readOnly;      
            });
        if(Ext.ux.Utility.isFunction(f.onClick))
            f.onClick = f.onClick.createInterceptor(function(){
                if(f._readOnly){
                    this.el.dom.checked = f.checked;
                }
                return !f._readOnly;      
            });
        if(Ext.ux.Utility.isFunction(f.setValue) && f instanceof Ext.form.Checkbox)
            f.setValue = f.setValue.createInterceptor(function(){ 
                return !f._readOnly;      
            }); 
    }
} 



;//Thanks: http://extjs.com/forum/showthread.php?p=165437
Ext.form.Action.JSONSubmit = function(form, options) {
	Ext.form.Action.JSONSubmit.superclass.constructor.call(this, form, options);
};

Ext.extend(Ext.form.Action.JSONSubmit, Ext.form.Action.Submit, {
	type : 'submit',

	run : function() {
		var o = this.options;
		var method = this.getMethod();
		var isGet = method == 'GET';
		if (o.clientValidation === false || this.form.isValid()) {
			Ext.Ajax.request(Ext.apply(this.createCallback(o), {
				form:this.form.el.dom,
				url : this.getUrl(isGet),
				method : method,
				headers : o.headers,
				params : !isGet ? this.getParams() : null,
				isUpload: this.form.fileUpload,
				jsonData : Ext.util.JSON.encode(Ext.apply(this.form
						.getResponseData(), this.form.getValues()))
			}));
		} else if (o.clientValidation !== false) { // client validation failed
			this.failureType = Ext.form.Action.CLIENT_INVALID;
			this.form.afterAction(this, false);
		}
	}
});
Ext.form.Action.ACTION_TYPES = {
	'load' : Ext.form.Action.Load,
	'submit' : Ext.form.Action.JSONSubmit
};
Ext.override(Ext.form.BasicForm, {
	setResponse : function(value) {
		this.rawresponse = value;
	},
	getResponseData : function() {
		return (this.rawresponse ? (Ext.util.JSON
				.decode(this.rawresponse.responseText)).data : {});
	}
});
//EOP
;/**
 * 表单基类
 */
divo.form.DivoBasicForm = Ext.extend(Ext.Component, {
	item : null,
	baseUrl : "",
	defaultDateFormat : "Y.m.d",
	alternateRows : false,
	separator : ":",
	fields : [],
	tableClass : "j-form-table",
	headClass : "",
	tbodyClass : "",
	labelHeaderClass : "labelHeader",
	valueHeaderClass : "valueHeader",
	labelClass : "label",
	valueClass : "",
	labelAlign : "right",
	labelWidth : "20%",
	rowClass : "",
	rowAlternateClass : "alt",
	fieldDefinitions : null,
	getFieldFunc : null,
	setFieldFunc : null,
	readOnly : false,
	iconRequired : null,
	active : true,
	clickable : false,
	defaultTextHeight : "300px",
	defaultTextWidth : "100%",
	//初始化
	initComponent : function() {
		Ext.applyIf(this, {
			baseUrl : this.getAppContext().baseUrl,
			defaultDateFormat : this.getAppContext().getDayMonthYearFormat()
		});

		divo.form.DivoBasicForm.superclass.initComponent.call(this)
	},
	getTypeFromString : function(s) {
		var ss = s.split("."), i = 0, obj = dj_global; //dj_global?
		do {
			obj = obj[ss[i++]];
		} while (i < ss.length && obj);
		return (obj != dj_global) ? obj : null;
	},
	_meta : {
		field : null,
		format : null,
		dataType : String,
		label : null,
		align : "left",
		valign : "top",
		getField : function() {
			return this.field || this.label;
		},
		getType : function() {
			return this.dataType;
		},
		idField : "id",
		displayField : "name",
		cls : null
	},
	setFieldDefinitions : function(defs) {
		delete this.fields;
		this.fields = [];
		for (var i = 0; i < defs.length; i++) {
			var def = Ext.apply({}, defs[i]);
			this.fields.push(this.createMetaData(def));
		}
		this.reconfig = true;
	},
	createMetaData : function(obj) {
		for (var p in this._meta) {
			if (!obj[p]) {
				obj[p] = this._meta[p];
			}
		}
		if (!obj.label) {
			obj.label = obj.field;
		}
		return obj;
	},
	createHeader : function(o) {
		var head = this.domNode.getElementsByTagName("thead")[0];
		while (head.childNodes.length > 0) {
			head.removeChild(head.childNodes[0]);
		}
		DomHelper.append(head, {
			tag : "tr",
			children : [{
				tag : "td",
				cls : this.labelHeaderClass,
				html : "&nbsp;"
			}, {
				tag : "td",
				cls : this.valueHeaderClass,
				html : "&nbsp;"
			}]
		});
	},
	createRow : function(meta, item) {
		var tpls = this.templates;
		var rowTpl1 = tpls.rowTpl1;
		var rowTpl2 = tpls.rowTpl2;
		var rowTpl3 = tpls.rowTpl3;
		var child;
		var tbody = this.tbody;
		var sep = meta.separator || this.separator;
		var val = this.getFieldFunc(item, meta);
		var label = meta.required && !this.readOnly
				? divo.required + meta.label
				: meta.label;
		if (meta.labelAlign != "top") {
			var row1 = rowTpl1.append(tbody, {
				labelCls : this.labelClass,
				labelValue : label,
				separator : sep,
				align : meta.labelAlign || this.labelAlign,
				style : "width:" + (meta.labelWidth || this.labelWidth) + ";"
			});
			child = row1.childNodes[1];
		} else {
			rowTpl2.append(tbody, {
				labelCls : this.labelClass,
				separator : sep,
				labelValue : label
			});
			var row2 = rowTpl3.append(tbody, {});
			child = row2.firstChild;
		}
		this.fillCell(child, meta, val);
		return row1;
	},
	fillCell : function(cell, meta, val) {
		if (this.readOnly || meta.readOnly) {
			this.fillCellReadOnly(cell, meta, val);
		} else {
			this.fillCellWithControl(cell, meta, val);
		}
	},
	fillCellReadOnly : function(cell, meta, val) {
		if (meta.sortType == "__markup__") {
			cell.innerHTML = val;
		} else {
			if (meta.getType() == Date || meta.getType() == "date") {
				cell.innerHTML = val
			} else {
				if (meta.getType() == "Lookup" || meta.dataType == "lookup"
						|| meta.dataType == "multiRadioBox"
						|| (meta.getType() == "Integer" && meta.options)) {
					if (val) {
						if (!val[meta.idField]) {
							var val = this.getLookup(meta, val);
						}
						val = val ? val[meta.displayField] : "";
						cell.appendChild(document.createTextNode(val));
					} else {
						cell.innerHTML = "";
					}
				} else {
					if ("Number number int Integer float Float".indexOf(meta.getType()) > -1) {
						if (val.length == 0) {
							val = "0";
						}
						var n = parseFloat(val, 10) + "";
						if (n.indexOf(".") > -1) {
							n = Math.round(parseFloat(val, 10), 2);
						}
						cell.innerHTML = n;
					} else {
						if (meta.getType() == "boolean") {
							cell.appendChild(document.createTextNode(val ? "是" : "否"));
						} else {
							if (meta.getType() == "text") {
								if (meta.controlTypeId == 2) {
									cell.innerHTML = this.encodeTextForDisplay(val);
								} else {
									val = "<div class=\"j-text-box\">" + val + "</div>";
									cell.innerHTML = val;
								}
							} else {
								if (this.clickable && meta.label == "ID") {
									cell.appendChild(this
											.makeValueHyperlink(val));
								} else {
									if (meta.inputType == "password") {
										val = this.createMask(val);
										cell.innerHTML = val;
									} else {
										cell.innerHTML = val;
									}
								}
							}
						}
					}
				}
			}
		}
	},
	createMask : function(val) {
		var mask = "";
		if (val) {
			for (var i = 0, len = val.length; i < len; i++) {
				mask += "*";
			}
		}
		return mask;
	},
	makeValueHyperlink : function(val) {
		return val;
	},
	fillCellWithControl : function(cell, meta, val) {
		if (meta.sortType == "__markup__") {
			cell.innerHTML = val;
		} else {
			if (meta.getType() == Date || meta.getType() == "date") {
				var fmt = meta.format || this.defaultDateFormat;
				this.createDatePicker(cell, meta, fmt, val);
			} else {
				if (meta.getType() == "Lookup" || meta.dataType == "lookup"
						|| (meta.getType() == "Integer" && meta.options)) {
					this.createDropDown(cell, meta, val);
				} else {
					if ("Number number int Integer float Float".indexOf(meta.getType()) > -1) {
						if (val.length == 0) {
							val = "0";
						}
						var n = parseFloat(val, 10) + "";
						if (n.indexOf(".") > -1) {
							n = Math.round(parseFloat(val, 10), 2);
						}
						cell.innerHTML = n;
					} else {
						if (meta.getType() == "boolean") {
							this.createCheckBox(cell, meta, val);
						} else {
							if (meta.getType() == "multiCheckboxes") {
								this.createMultiCheckboxes(cell, meta, val);
							} else {
								if (meta.getType() == "multiRadioBox") {
									this.createMultiRadioBox(cell, meta, val);
								} else {
									if (meta.getType() == "text") {
										val = val ? val : "";
										if (meta.controlTypeId == 2) {
											this.createTextarea(cell, meta,	val);
										}
									} else {
										this.createInputBox(cell, meta, val);
									}
								}
							}
						}
					}
				}
			}
			if (meta.required && this.iconRequired) {
				cell.appendChild(this.createImage(this.iconRequired));
			}
		}
	},
	createDropDown : function(cell, meta, val) {
		var selectEl = document.createElement("select");
		var idField = meta.idField;
		var displayField = meta.displayField;
		for (var i = 0; i < meta.options.length; i++) {
			var option = meta.options[i];
			var ele = document.createElement("option");
			ele.value = option[idField];
			ele.innerHTML = option[displayField];
			selectEl.appendChild(ele);
		}
		if (val) {
			selectEl.value = val ? (val[idField] ? val[idField] : val) : "";
		} else {
			var s = this.getLookupByDisplay(meta, "Select One");
			selectEl.value = s ? s[meta.idField] : "";
		}
		cell.appendChild(selectEl);
		meta.control = selectEl;
	},
	createMultiCheckboxes : function(cell, meta, val) {
		var idField = meta.idField;
		var displayField = meta.displayField;
		var flds = [];
		if (typeof(val) == "string") {
			flds = val.split(" ");
		} else {
			if (meta.returnIds) {
				flds = val;
			} else {
				for (var i = 0; i < val.length; i++) {
					flds.push(val[i][idField]);
				}
			}
		}
		var div = Ext.DomHelper.append(cell, {
			tag : "div",
			cls : "j-form-scroll-box",
			id : this.getEl().id + "_" + meta.field
		}, true);
		if (!this.checkboxTpl) {
			this.checkTpl = new Ext.Template("<input type=\"checkbox\" name=\"{fieldName}\" value=\"{id}\""
					+ "{checked}/>&nbsp;&nbsp;{name}<br/>");
		}
		var i, tpl = this.checkTpl;
		for (i = 0; i < meta.options.length; i++) {
			var option = meta.options[i];
			var state = (this.hasValue(option[idField], flds))
					? "checked=\"checked\""
					: "";
			tpl.append(div, {
				filedName : meta.field,
				id : option[idField],
				name : option[displayField],
				checked : state
			});
		}
		if (i < 7) {
			div.dom.style.height = i * 20 + "px";
		}
		meta.control = div.dom;
	},
	hasValue : function(v, flds) {
		for (var i in flds) {
			if (flds[i] == v) {
				return true;
			}
		}
		return false;
	},
	encodeTextForDisplay : function(text, html) {
		if (text == undefined || text == null) {
			return text;
		}
		if (html == undefined) {
			html = "</br>";
		}
		text = text.replace(/\r\n/g, html);
		text = text.replace(/\n/g, html);
		text = text.replace(/\r/g, html);
		return text;
	},
	createInputBox : function(cell, meta, val) {
		var inputEl = document.createElement("input");
		if (meta.inputType) {
			inputEl.type = meta.inputType;
		} else {
			inputEl.type = "text";
		}
		inputEl.value = val;
		inputEl.id = this.widgetId + "_" + meta.field;
		inputEl.name = inputEl.id;
		if (meta.size) {
			inputEl.size = meta.size;
		} else {
			if (meta.style) {
				inputEl.setAttribute("style", meta.style);
			} else {
				inputEl.style.width = "99%";
			}
		}
		inputEl.maxLength = meta.maxLength ? meta.maxLength : 255;
		cell.appendChild(inputEl);
		meta.control = inputEl;
	},
	createTextarea : function(cell, meta, val) {
		var textareaEl = document.createElement("textarea");
		cell.appendChild(textareaEl);
		textareaEl.value = val;
		textareaEl.id = this.widgetId + "_" + meta.field;
		textareaEl.name = textareaEl.id;
		textareaEl.style.height = meta.height ? meta.height : 120;
		textareaEl.style.width = meta.width ? meta.width : "99%";
		meta.control = textareaEl;
	},
	createHtmlEditor : function(cell, meta, val) {
		var divEl = document.createElement("div");
		cell.appendChild(divEl);
		var editor = new Ext.form.HtmlEditor({
			name : meta.field,
			width : meta.width ? meta.width : "100%",
			height : meta.height ? meta.height : 200
		});
		editor.render(divEl);
		editor.setValue(val);
		meta.control = editor;
		meta.control.controlType = "htmleditor";
	},
	createCheckBox : function(cell, meta, val) {
		var inputEl = document.createElement("input");
		inputEl.type = "checkbox";
		cell.appendChild(inputEl);
		inputEl.value = "on";
		if (val) {
			inputEl.checked = val;
		}
		inputEl.id = this.widgetId + "_" + meta.field;
		inputEl.name = inputEl.id;
		meta.control = inputEl;
	},
	createDatePicker : function(cell, meta, fmt, val) {
		var divEl = document.createElement("div");
		cell.appendChild(divEl);
		var fld = new Ext.form.DateField({
			format : fmt,
			allowBlank : true
		});
		fld.render(divEl);
		fld.setValue(val);
		meta.control = fld;
		meta.widgetType = "ext";
	},
	createMultiRadioBox : function(cell, meta, val) {
		var id = this.getEl().id;
		if (typeof(val) == "object") {
			val = val[meta.idField];
		}
		var div = Ext.DomHelper.append(cell, {
			tag : "div",
			cls : (meta.cls != null) ? meta.cls : "j-form-scroll-box",
			id : id + "_" + meta.field
		}, true);
		if (!this.radioTpl) {
			this.radioTpl = new Ext.Template("<input type=\"radio\" name=\"{fieldName}\" value=\"{value}\""
					+ "{checked}{disabled}/>&nbsp;&nbsp;{name}<br/>");
		}
		var i, tpl = this.radioTpl;
		var _55f = id + "_r_" + meta.field;
		for (i = 0; i < meta.options.length; i++) {
			var _560 = meta.options[i];
			var _561 = (val == _560[meta.idField]) ? "checked=\"checked\"" : "";
			var _562 = (_560.disable ? " disabled" : "");
			tpl.append(div, {
				fieldName : _55f,
				value : _560[meta.idField],
				name : _560[meta.displayField],
				checked : _561,
				disabled : _562
			});
		}
		meta.control = div.dom;
	},
	createImage : function(src) {
		var imgEl = document.createElement("img");
		imgEl.src = src;
		imgEl.alt = "required";
		return imgEl;
	},
	getLookup : function(meta, val) {
		var result = null;
		if (val) {
			for (var i in meta.options) {
				var option = meta.options[i];
				if (option[meta.idField] == val) {
					result = option;
					break;
				}
			}
		}
		return result;
	},
	getLookupByDisplay : function(meta, val) {
		var result = null;
		if (val) {
			for (var i in meta.options) {
				var option = meta.options[i];
				if (option[meta.displayField] == val) {
					result = option;
					break;
				}
			}
		}
		return result;
	},
	getMultiCheckboxesValues : function(meta, val) {
		var inputEl = meta.control.getElementsByTagName("input");
		var vs = [];
		var idField = meta.idField;
		for (var i = 0; i < inputEl.length; i++) {
			var fld = inputEl[i];
			if (fld.checked) {
				if (meta.returnIds || meta.returnString) {
					vs.push(fld.value);
				} else {
					var obj = {};
					obj[idField] = fld.value;
					vs.push(obj);
				}
			}
		}
		if (meta.returnString) {
			return vs.join(",");
		} else {
			return vs;
		}
	},
	getMultiRadioBoxValue : function(meta, val) {
		var inputEl = meta.control.getElementsByTagName("input");
		var vs = null;
		for (var i = 0; i < inputEl.length; i++) {
			var fld = inputEl[i];
			if (fld.checked) {
				vs = fld.value;
				break;
			}
		}
		return vs;
	},
	getFieldMeta : function(name) {
		for (var i = 0; i < this.fields.length; i++) {
			var meta = this.fields[i];
			if (meta.field == name) {
				return meta;
			}
		}
		return null;
	},
	getField : function(obj, meta) {
		var name = meta.field;
		var map = meta.mapping;
		var fld = null;
		if (map) {
			fld = map(obj);
		} else {
			fld = obj[name];
		}
		if (typeof fld == "boolean") {
			return fld;
		}
		return fld ? fld : "";
	},
	setField : function(obj, meta, fld) {
		obj[meta.field] = fld;
	},
	encodeTextForTextarea : function(text, html) {
		if (text == undefined || text == null) {
			return text;
		}
		if (html == undefined) {
			html = "</br>";
		}
		text = text.replace(/\r\n/g, html);
		text = text.replace(/\n/g, html);
		text = text.replace(/\r/g, html);
		return text;
	},
	getUpdatedItem : function() {
		var item = this.cloneItem(this.item);
		for (var i = 0; i < this.fields.length; i++) {
			var meta = this.fields[i];
			if (meta.control) {
				var v;
				if (meta.dataType == Date || meta.dataType == "date") {
					if (meta.control.getValue()) {
						//v = meta.control.getValue();
						v = meta.control.getRawValue()
					} else {
						v = null;
					}
				} else {
					if (meta.dataType == "lookup" || meta.dataType == "Lookup") {
						v = this.getLookup(meta, meta.control.value);
						if (v) {
							v = {
								id : v.id,
								name : v.name
							};
						}
					} else {
						if (meta.dataType == "multiCheckboxes") {
							v = this.getMultiCheckboxesValues(meta);
						} else {
							if (meta.dataType == "multiRadioBox") {
								v = this.getMultiRadioBoxValue(meta);
							} else {
								if (meta.dataType == "boolean") {
									v = meta.control.checked;
								} else {
									if (meta.dataType == "text") {
										if (meta.control.controlType == "htmleditor") {
											v = meta.control.getValue();
										} else {
											v = meta.control.value;
										}
									} else {
										v = meta.control.value;
									}
								}
							}
						}
					}
				}
				this.setFieldFunc(item, meta, v);
			}
		}
		return item;
	},
	setFocus : function() {
		if (this.readOnly) {
			return;
		}
		for (var i = 0; i < this.fields.length; i++) {
			var meta = this.fields[i];
			if (meta.control) {
				var tag = meta.control.tagName;
				if (tag == "INPUT" || tag == "SELECT" || tag == "TEXTAREA") {
					meta.control.focus();
					break;
				}
			}
		}
	},
	getItemChanges : function() {
		if (this.readOnly) {
			return null;
		}
		var itemChanges = "";
		if (!this.active) {
			return false;
		}
		var updatedItem = this.getUpdatedItem();
		for (var i in this.fields) {
			var meta = this.fields[i];
			var itemOldValue = this.item[meta.field];
			var itemValue = updatedItem[meta.field];
			var hasChanged = false;
			if (meta.dataType == "Lookup") {
				if (itemOldValue == null) {
					hasChanged = (itemValue != null);
				} else {
					if (itemValue == null) {
						hasChanged = (itemOldValue != null);
					} else {
						hasChanged = (itemOldValue.id != itemValue.id);
					}
				}
			} else {
				if (!itemOldValue && !itemValue) {
					hasChanged = false;
				} else {
					hasChanged = (itemOldValue != itemValue);
				}
			}
			if (hasChanged) {
				if (itemChanges) {
					itemChanges += ", ";
				}
				itemChanges += meta.label;
			}
		}
		return itemChanges;
	},
	cloneItem : function(item) {
		return Ext.apply({}, item);
	},
	destroyWidgetControls : function() {
		for (var j = 0; j < this.fields.length; j++) {
			var meta = this.fields[j]
			if (meta.control && meta.control.widgetType == "ext") {
				Ext.destroy(meta.control)
			}
		}
		return true
	},
	setItem : function(item) {
		this.removeFields();
		this.item = item;
		this.renderRows(this.tbody, item);
		this.reconfig = false;
	},
	removeFields : function() {
		var body = Ext.getDom(this.tbody);
		while (body.childNodes.length > 0) {
			body.removeChild(body.childNodes[0]);
		}
		this.destroyWidgetControls();
	},
	setValues : function(item) {
		if (this.reconfig) {
			this.setValues(item);
		} else {
			for (var i = 0; i < this.fields.length; i++) {
				var meta = this.fields[i];
				if (meta.control) {
					val = this.getFieldFunc(item, meta);
					if (meta.dataType == Date || meta.dataType == "date") {
						meta.control.setValue(value);
					} else {
						if (meta.dataType == "lookup" || meta.dataType == "Lookup") {
							meta.contorl.setValue(value ? value.id : null);
						} else {
							if (meta.dataType == "multiCheckboxes") {
								value = this.getMultiCheckboxesValues(meta);
							} else {
								if (meta.dataType == "multiRadioBox") {
									value = this.getMultiRadioBoxValue(meta);
								} else {
									if (meta.dataType == "boolean") {
										meta.control.checked = value;
									} else {
										if (meta.dataType == "text") {
											if (meta.control.controlType == "htmleditor") {
												meta.control.setValue(value);
											} else {
												meta.control.value = value;
											}
										} else {
											meta.control.value = value;
										}
									}
								}
							}
						}
					}
				}
			}
		}
	},
	renderRows : function(body, item) {
		for (var j = 0; j < this.fields.length; j++) {
			var meta = this.fields[j];
			if (!meta.hidden && !(this.readOnly && meta.hideReadOnly)) {
				this.createRow(meta, item);
			}
		}
	},
	initTemplates : function() {
		if (!this.templates) {
			if (!divo.form.DivoBasicForm.tpls) {
				divo.form.DivoBasicForm.tpls = {};
				var tpls = divo.form.DivoBasicForm.tpls;
				tpls.rowTpl1 = new Ext.Template("<tr><td class=\"{labelCls}\" align=\"{align}\" style=\"{style}\">{labelValue}{separator}</td><td></td></tr>");
				tpls.rowTpl2 = new Ext.Template("<tr><td class=\"{labelCls}\" colspan=\"2\">{labelValue}{separator}</td></tr>");
				tpls.rowTpl3 = new Ext.Template("<tr><td colspan=\"2\"></td></tr>");
			}
			this.templates = divo.form.DivoBasicForm.tpls;
		}
	},
	getHeaderPanel : function(toShow) {
		if (toShow) {
			this.headerPanel.show();
		}
		return this.headerPanel
	},
	onRender : function(ct) {
		ct = Ext.get(ct);
		this.headerPanel = Ext.DomHelper.append(ct,	"<div class=\"x-grid-topbar \"></div>",true);
		this.headerPanel.enableDisplayMode("block");
		this.el = ct.createChild({
			tag : "table",
			cls : this.tableClass,
			id : this.id || Ext.id()
		});
		this.table = this.el;
		this.domNode = this.el.dom;
		this.tbody = Ext.DomHelper.append(this.table, {
			tag : "tbody"
		});
		if (this.fieldDefinitions) {
			this.setFieldDefinitions(this.fieldDefinitions);
		}
		if (this.getFieldFunc == null) {
			this.getFieldFunc = this.getField;
		}
		if (this.setFieldFunc == null) {
			this.setFieldFunc = this.setField;
		}
		this.initTemplates();
	},
	beforeDestroy : function() {
		this.destroyWidgetControls();
		divo.form.DivoBasicForm.superclass.beforeDestroy.apply(this, arguments);
	},
	setSize : function() {
		//prevent error: item.setSize is not a function
		//caused by layoutOnTabChange
	}
});
// EOP
;/**
 * DivoForm
 */
divo.form.DivoForm = Ext.extend(divo.form.DivoBasicForm, {
	title : "",
	titlePanel : null,
	buttonPanel : null,
	editButton : null,
	cancelButton : null,
	saveButton : null,
	buttonList : new Array(),
	errorRow : null,
	errorPanel : null,
	editButtonLabel : "Edit",
	cancelButtonLabel : "Cancel",
	saveButtonLabel : "Save",
	formType : "both",
	showButtons : true,
	showTitle : true,
	buttonPanelAlign : "left",
	errorPanelAlign : "left",
	titlePanelAlign : "left",
	buttonPanelLocation : "bottom",
	buttonClass : "button",
	buttonPanelClass : "buttonPanel",
	errorPanelClass : "errorPanel",
	titlePanelClass : "titlePanel",
	
	createButtonPanel : function() {
		var tdEl = document.createElement("td");
		tdEl.setAttribute("align", this.buttonPanelAlign);
		this.editButton = this.createButton(tdEl, "edit", this.editButtonLabel);
		this.saveButton = this.createButton(tdEl, "save", this.saveButtonLabel);
		this.cancelButton = this.createButton(tdEl, "cancel",
				this.cancelButtonLabel);
		this.editButton.on("click", this.onEdit, this);
		this.cancelButton.on("click", this.onCancel, this);
		this.saveButton.on("click", this.onSave, this);
		if (this.readOnly) {
			this.cancelButton.hide();
			this.saveButton.hide();
		} else {
			this.editButton.hide();
		}
		if (this.buttonPanelClass) {
			tdEl.className = this.buttonPanelClass;
		}
		return tdEl;
	},
	createErrorPanel : function() {
		var tdEl = document.createElement("td");
		tdEl.setAttribute("align", this.errorPanelAlign);
		if (this.errorPanelClass) {
			tdEl.className = this.errorPanelClass;
		}
		return tdEl;
	},
	createTitlePanel : function() {
		var tdEl = document.createElement("td");
		tdEl.setAttribute("align", this.titlePanelAlign);
		if (this.titlePanelClass) {
			tdEl.className = this.titlePanelClass;
		}
		tdEl.innerHTML = this.title;
		return tdEl;
	},
	createErrorRow : function() {
		var row = document.createElement("tr");
		this.errorPanel.setAttribute("colSpan", 2);
		row.appendChild(this.errorPanel);
		return new Ext.Element(row);
	},
	createTitleRow : function() {
		var row = null;
		if (this.showTitle
				|| (this.showButtons && this.buttonPanelLocation == "top")) {
			row = document.createElement("tr");
			td = document.createElement("td");
			td.setAttribute("colSpan", 2);
			row.appendChild(td);
			var tableEl = document.createElement("table");
			tableEl.setAttribute("width", "100%");
			var bodyEl = document.createElement("tbody");
			var rowEl = document.createElement("tr");
			if (this.showTitle) {
				rowEl.appendChild(this.titlePanel);
			}
			if (this.showButtons && this.buttonPanelLocation == "top") {
				rowEl.appendChild(this.buttonPanel);
			}
			tableEl.appendChild(bodyEl);
			bodyEl.appendChild(rowEl);
			td.appendChild(tableEl);
		}
		return row;
	},
	createFooterRow : function() {
		var row = null;
		if (this.showButtons && this.buttonPanelLocation == "bottom") {
			row = document.createElement("tr");
			this.buttonPanel.setAttribute("colspan", 2);
			row.appendChild(this.buttonPanel);
		}
		return row;
	},
	createButton : function(tdEl, name, label) {
		var btn = Ext.DomHelper.append(tdEl, {
			tag : "input",
			type : "button",
			value : label,
			name : this.widgetId + "_" + name,
			cls : this.buttonClass
		}, true);
		return btn;
	},
	onEdit : function(e) {
		if (this.formType == "both") {
			this.readOnly = false;
			this.setItem(this.item);
			this.editButton.hide();
			this.saveButton.show();
			this.cancelButton.show();
		}
	},
	onCancel : function(e) {
		if (this.formType == "both") {
			this.readOnly = true;
			this.setItem(this.item);
			this.editButton.show();
			this.saveButton.hide();
			this.cancelButton.hide();
		}
	},
	displayErrorMessages : function(errors) {
		if (this.errorRow) {
			this.errorRow.show();
			var msg = "";
			for (var i = 0; i < errors.length; i++) {
				msg += "注意: " + errors[i] + "<br/>";
			}
			this.errorPanel.innerHTML = msg;
		}
	},
	clearErrorMessages : function() {
		if (this.errorRow) {
			this.errorRow.hide();
			this.errorPanel.innerHTML = "";
		}
	},
	onSave : function(e) {
		alert("onSave")
		this.doOnSave(this.getUpdatedItem());
	},
	doOnSave : function(item) {
		this.fireEvent("save", this, item);
	},
	dwrSave : function(_5c9, item) {
		//访问后台
		//_5c9(item, {
		//	callback : this.saveCallback.createDelegate(this),
		//	errorHandler : this.handleException.createDelegate(this)
		//});
	},
	saveCallback : function(item) {
		if (item) {
			this.item = item;
		} else {
			this.item = this.getUpdatedItem();
		}
		this.onCancel();
		this.fireEvent("afterSave", this, item ? item : this.item);
	},
	handleException : function(msg, ex) {
		var errors = ex.errors;
		if (errors != null && errors.length > 0) {
			this.displayErrorMessages(errors);
		}
	},
	setItem : function(item) {
		var body = this.tbody;
		this.removeFields();
		this.item = item;
		this.errorRow = this.createErrorRow();
		var titleRowEl = this.createTitleRow();
		var footerRowEl = this.createFooterRow();
		if (!this.readOnly) {
			body.appendChild(this.errorRow.dom);
			this.errorRow.hide();
		}
		if (this.showButtons) {
			this.editButton[this.readOnly ? "show" : "hide"]();
			this.saveButton[!this.readOnly ? "show" : "hide"]();
			this.cancelButton[!this.readOnly ? "show" : "hide"]();
		}
		this.renderRows(body, item);
		if (titleRowEl) {
			if (body.firstChild) {
				body.insertBefore(titleRowEl, body.firstChild);
			} else {
				body.appendChild(titleRowEl);
			}
		}
		if (footerRowEl) {
			body.appendChild(footerRowEl);
		}
	},
	onRender : function(ct, _5d4) {
		divo.form.DivoForm.superclass.onRender.call(this, ct, _5d4);
		this.readOnly = (this.formType != "editOnly");
		this.buttonPanel = this.createButtonPanel();
		this.errorPanel = this.createErrorPanel();
		this.titlePanel = this.createTitlePanel();
	},
	setMask : function() {
		if (this.container) {
			this.container.mask("Loading...");
		}
	},
	unmask : function() {
		if (this.container) {
			this.container.unmask();
		}
	}
});
//EOP
;//Thanks: http://extjs.com/forum/showthread.php?t=31989
Ext.ns('Ext.ux.form'); 

/**
 * @class Ext.ux.form.Spacer
 * @extends Ext.BoxComponent
 * Utility spacer class.
 * @constructor
 * @param {Number} height (optional) Spacer height in pixels (defaults to 22).
 */
Ext.ux.form.Spacer = Ext.extend(Ext.BoxComponent, {
  height: 22,
  autoEl: 'div' // thanks @jack =)
});

Ext.reg('spacer', Ext.ux.form.Spacer);
//EOP
;/**
 * A ComboBox with a secondary trigger button that clears the contents of the
 * ComboBox.
 * 
 * Thanks: http://extjs.com/forum/showthread.php?t=9619
 */
divo.form._ClearableComboBox = Ext.extend(Ext.form.ComboBox, {
	initComponent : function() {
		divo.form._ClearableComboBox.superclass.initComponent.call(this);

		this.triggerConfig = {
			tag : 'span',
			cls : 'x-form-twin-triggers',
			style : 'padding-right:2px', // padding needed to prevent IE from
			// clipping 2nd trigger button
			cn : [ {
				tag : "img",
				src : Ext.BLANK_IMAGE_URL,
				cls : "x-form-trigger"
			}, {
				tag : "img",
				src : Ext.BLANK_IMAGE_URL,
				cls : "x-form-trigger x-form-clear-trigger"
			}]
		};
	},

	getTrigger : function(index) {
		return this.triggers[index];
	},

	initTrigger : function() {
		var ts = this.trigger.select('.x-form-trigger', true);
		this.wrap.setStyle('overflow', 'hidden');
		var triggerField = this;
		ts.each(function(t, all, index) {
			t.hide = function() {
				var w = triggerField.wrap.getWidth();
				this.dom.style.display = 'none';
				triggerField.el.setWidth(w - triggerField.trigger.getWidth());
			};
			t.show = function() {
				var w = triggerField.wrap.getWidth();
				this.dom.style.display = '';
				triggerField.el.setWidth(w - triggerField.trigger.getWidth());
			};
			var triggerIndex = 'Trigger' + (index + 1);

			if (this['hide' + triggerIndex]) {
				t.dom.style.display = 'none';
			}
			t.on("click", this['on' + triggerIndex + 'Click'], this, {
				preventDefault : true
			});
			t.addClassOnOver('x-form-trigger-over');
			t.addClassOnClick('x-form-trigger-click');
		}, this);
		this.triggers = ts.elements;
	},

	onTrigger1Click : function() {
		this.onTriggerClick()
	}, // pass to original combobox trigger handler
		onTrigger2Click : function() {
			this.reset()
		} // clear contents of combobox
}); //divo.form._ClearableComboBox

/**
 * 可指定 searchField 属性的下拉列表框，带有清除输入值功能
 */
divo.form.ClearableComboBox = function(config) {
	divo.form.ComboBox.superclass.constructor.call(this, config);
};

Ext.extend(divo.form.ClearableComboBox, divo.form._ClearableComboBox, {
	searchField : undefined,

	    //重写父类方法
	doQuery : function(C, B) {
		if (C === undefined || C === null) {
			C = ""
		}
		var A = {
			query : C,
			forceAll : B,
			combo : this,
			cancel : false
		};
		if (this.fireEvent("beforequery", A) === false || A.cancel) {
			return false
		}
		C = A.query;
		B = A.forceAll;
		if (B === true || (C.length >= this.minChars)) {
			if (this.lastQuery !== C) {
				this.lastQuery = C;
				if (this.mode == "local") {
					this.selectedIndex = -1;
					if (B) {
						this.store.clearFilter()
					} else {
						this.store.filter(this.searchField, C) //displayField --> searchField
					}
					this.onLoad()
				} else {
					this.store.baseParams[this.queryParam] = C;
					this.store.load( {
						params : this.getParams(C)
					});
					this.expand()
				}
			} else {
				this.selectedIndex = -1;
				this.onLoad()
			}
		}
	}

}); // Ext.extend

//EOP
;// Thanks: http://extjs.com/forum/showthread.php?t=12030
Ext.namespace("Ext.ux.form");

Ext.ux.form.HistoryClearableComboBox = function(config) {

	Ext.ux.form.HistoryClearableComboBox.superclass.constructor.call(this,
			config);

	this.addEvents( {
		'change' : true,
		'clearvalue' : true
	});

	this.store = new Ext.data.SimpleStore( {
		fields : ['query'],
		data : []
	});

	this.historyRecord = Ext.data.Record.create([{
		name : 'query',
		type : 'string'
	}]);

	this.triggerConfig = {
		tag : 'span',
		cls : 'x-form-twin-triggers',
		style : 'padding-right:2px', // padding needed to prevent IE from
		// clipping 2nd trigger button
		cn : [ {
			tag : "img",
			src : Ext.BLANK_IMAGE_URL,
			cls : "x-form-trigger",
			style : config.hideComboTrigger ? "display:none" : ""
		}, {
			tag : "img",
			src : Ext.BLANK_IMAGE_URL,
			cls : "x-form-trigger x-form-clear-trigger",
			style : config.hideClearTrigger ? "display:none" : ""
		}]
	};
};

Ext.extend(Ext.ux.form.HistoryClearableComboBox, Ext.form.ComboBox, {

	store : undefined,
	displayField : 'query',
	typeAhead : false,
	mode : 'local',
	triggerAction : 'all',
	hideTrigger : false,
	historyRecord : undefined,
	maxInHistory : 10,
	rememberOn : 'enter',

	getTrigger : function(index) {
		return this.triggers[index];
	},

	initTrigger : function() {
		var ts = this.trigger.select('.x-form-trigger', true);
		this.wrap.setStyle('overflow', 'hidden');
		var triggerField = this;
		ts.each(function(t, all, index) {
			t.hide = function() {
				var w = triggerField.wrap.getWidth();
				this.dom.style.display = 'none';
				triggerField.el.setWidth(w - triggerField.trigger.getWidth());
			};
			t.show = function() {
				var w = triggerField.wrap.getWidth();
				this.dom.style.display = '';
				triggerField.el.setWidth(w - triggerField.trigger.getWidth());
			};
			var triggerIndex = 'Trigger' + (index + 1);

			if (this['hide' + triggerIndex]) {
				t.dom.style.display = 'none';
			}
			t.on("click", this['on' + triggerIndex + 'Click'], this, {
				preventDefault : true
			});
			t.addClassOnOver('x-form-trigger-over');
			t.addClassOnClick('x-form-trigger-click');
		}, this);
		this.triggers = ts.elements;
	},

	onTrigger1Click : function() {
		this.onTriggerClick()
	}, // pass to original combobox trigger handler

		onTrigger2Click : function() {
			this.reset();
			this.fireEvent('clearvalue', this);
		}, // clear contents of combobox

		onRender : function(ct) {
			Ext.ux.form.HistoryClearableComboBox.superclass.onRender.call(this,
					ct);
		},

		initEvents : function() {
			Ext.ux.form.HistoryClearableComboBox.superclass.initEvents
					.call(this);
			this.el.on("keyup", this.onHistoryKeyUp, this);
		},

		historyChange : function(value) {
			var value = this.getValue().replace(/^\s+|\s+$/g, "");
			if (value.length == 0)
				return;
			this.store.clearFilter();
			var vr_insert = true;
			if (this.rememberOn == "all") {
				this.store.each(function(r) {
					if (r.data['query'].indexOf(value) == 0) {
						// backspace
						vr_insert = false;
						return false;
					} else if (value.indexOf(r.data['query']) == 0) {
						// forward typing
						this.store.remove(r);
					}
				});
			}
			if (vr_insert == true) {
				this.store.each(function(r) {
					if (r.data['query'] == value) {
						vr_insert = false;
					}
				});
			}
			if (vr_insert == true) {
				var vr = new this.historyRecord( {
					query : value
				});
				this.store.insert(0, vr);
			}
			var ss_max = this.maxInHistory;
			if (this.store.getCount() > ss_max) {
				var ssc = this.store.getCount();
				var overflow = this.store.getRange(ssc - (ssc - ss_max), ssc);
				for (var i = 0;i < overflow.length; i++) {
					this.store.remove(overflow[i]);
				}
			}
		},

		onHistoryKeyUp : function(e) {
			if ((this.rememberOn == "enter" && e.getKey() == 13)
					|| (this.rememberOn == "all")) {
				this.historyChange(this.getValue());
				this.fireEvent('change', this.getValue());
			}
		},

		setValue : function(v) {
			Ext.ux.form.HistoryClearableComboBox.superclass.setValue.call(this,
					v);
			this.fireEvent('change', this.getValue());
		},

		clearValue : function() {
			this.setValue("");
		},

		EOJ : function() {
		}
	});

// EOP
;Ext.namespace('Ext.ux.form');

/**
Thanks: http://madrabaz.com/ext/ux/LovField/demo/

        http://extjs.com/forum/showthread.php?t=26146&page=3 
        (添加了 displayValue (displays selected values in the text field)
                hiddenValue (sets the hidden value, ID's, to be posted.)
                
        本人修改为只支持网格。                

Config Options
--------------

alwaysLoadStore : True to reload data store each time the LOV is opened(default to false)

displayField : The data store field name or Ext.XTemplate instance or config option to use for a display(default to 'id')

displaySeparator : String to separate selected item displays(defaults to ',' if textarea = false or '\n' if textarea = true)

lovHeight : LOV window width (default to 300)

lovTitle : LOV window title (default to '')

lovWidth : LOV window width (default to 300)

maxItem : Maximum selected item number required(default to Number.MAX_VALUE)

maxItemText : Error text to display if the maximum select validation fails (default to 'The maximum selected item number for this field is {0}')

minItem : Minimum selected item number required(default to 0)

minItemText : Error text to display if the minimum select validation fails (default to 'The minimum selected item number for this field is {0}')

multiSelect : True to allow selection of more than one item at a time (default to false)

qtipTpl : Ext.XTemplate instance or config option to display the qtip(default to undefined)

showOnFocus : True to show LOV window when user focus the field(default to false)

textarea : True to display the textarea field instead of text field (default to false)

valueField : The data store field name to use for a value(default to 'id')

valueSeparator : String to separate selected item values(default to ',')

view : The Ext.grid.GridPanel or Ext.DataView to bind this LovField

windowConfig : LOV window config options(autoScroll, layout, bbar and items options are not changed)

displayValue: 

hiddenValue:

isReadOnly: 代替 readOnly 设置只读（不让选择）

*/

Ext.ux.form.LovField = Ext.extend(Ext.form.TriggerField, {
    defaultAutoCreate : {tag: "input", type: "text", size: "16",style:"cursor:default;", autocomplete: "off"},
    triggerClass: 'x-form-search-trigger',
    validateOnBlur: false,

    // LOV window width
    lovWidth: 300,
    // LOV window height
    lovHeight: 300,
    // LOV window title
    lovTitle: '',
    // Multiple selection is possible?
    multiSelect: false,

    // If this option is true, data store reloads each time the LOV opens
    alwaysLoadStore: false,
    // LOV data provider, intance of Ext.grid.GridPanel or Ext.DataView
    view: {},

    displayValue: '',
    hiddenValue: '',
    
    // Which data store field will use for return
    valueField: 'id',
    // Which data store field will use for display
    displayField: 'id',
    // If multiple items are selected, they are joined with this character
    valueSeparator: ',',
    displaySeparator: ',',

    // LOV window configurations
    // autoScroll, layout, bbar and items configurations are not changed by this option
    windowConfig: {},

    showOnFocus : false,

    minItem : 0,
    minItemText : '至少应该选择{0}项',
    minItemText2 : '至少应该选择{0}项，最多允许选择{1}项',

    maxItem : Number.MAX_VALUE,
    maxItemText : '最多允许选择{0}项',

    // Private
    isStoreLoaded: false,
    // Private
    selections: [],
    // Private
    selectedRecords: null,

    initComponent: function(){
       	this.hiddenName = this.hiddenName || this.name //保证FindField能找到
        this.multiSelect = this.maxItem > 1
        
        Ext.ux.form.LovField.superclass.initComponent.call(this);
    },

    onRender: function(ct, position){
        if (this.isRendered) {
            return;
        }

        this.readOnly = true;
        if(this.textarea){
            this.defaultAutoCreate = {tag: "textarea", style:"cursor:default;width:124px;height:65px;", autocomplete: "off", value: this.displayValue};
            this.displaySeparator = '\n';
        }
        this.value = this.displayValue;

        Ext.ux.form.LovField.superclass.onRender.call(this, ct, position);

        this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden',
        name: this.el.dom.getAttribute('name'), id: this.id + '-hidden'}, 'before', true);
        this.hiddenField.value = this.hiddenValue;

        // prevent input submission
        this.el.dom.removeAttribute('name');

        if(this.showOnFocus){
            this.on('focus',this.onTriggerClick,this);
        }

        this.isRendered = true;
    },
    
    afterRender : function() {
		Ext.ux.form.LovField.superclass.afterRender.apply(this,arguments)
    	
        this.viewType = 'grid'
        
        if(this.viewType == 'grid') {
            this.view.sm = this.view.getSelectionModel();
            this.view.store = this.view.getStore();
        }

        if(this.viewType == 'grid'){
            this.view.sm.singleSelect = !this.multiSelect;
        }else{
            this.view.singleSelect = !this.multiSelect;
            this.view.multiSelect = this.multiSelect;
        }

        if(Ext.type(this.displayField) == 'array'){
            this.displayField = this.displayField.join('');
        }
        if (/<tpl(.*)<\/tpl>/.test(this.displayField) && !(this.displayFieldTpl instanceof Ext.XTemplate)) {
            this.displayFieldTpl = new Ext.XTemplate(this.displayField).compile();
        }

        if(Ext.type(this.qtipTpl) == 'array'){
            this.qtipTpl = this.qtipTpl.join('');
        }
        if(/<tpl(.*)<\/tpl>/.test(this.qtipTpl) && !(this.qtipTpl instanceof Ext.XTemplate) ){
            this.qtipTpl = new Ext.XTemplate(this.qtipTpl).compile();
        }

        // If store was auto loaded mark it as loaded
        if (this.view.store.autoLoad) {
            this.isStoreLoaded = true;
        }
        
    },

    validateValue : function(value){
    	if (this.hiddenValue) return true
    	
        if( Ext.ux.form.LovField.superclass.validateValue.call(this, value)){
            if(this.selectedRecord && this.selectedRecords.length < this.minItem){
            	if (this.maxItem != Number.MAX_VALUE)
                    this.markInvalid(String.format(this.minItemText2, this.minItem, this.maxItem));
                else    
                    this.markInvalid(String.format(this.minItemText, this.minItem));
                return false;
            }
            if(this.selectedRecord && this.selectedRecords.length > this.maxItem){
                this.markInvalid(String.format(this.maxItemText, this.maxItem));
                return false;
            }
        }else{
            return false;
        }
        return true;
    },

    getSelectedRecords : function(){
        if(this.viewType == 'grid'){
            this.selections = this.selectedRecords = this.view.sm.getSelections();
        }else{
            this.selections = this.view.getSelectedIndexes();
            this.selectedRecords = this.view.getSelectedRecords();
        }

        return this.selectedRecords;
    },

    clearSelections : function(){
        return (this.viewType == 'grid')? this.view.sm.clearSelections() : this.view.clearSelections();
    },

    select : function(selections){
        if(this.viewType == 'grid'){
            if(selections[0] instanceof Ext.data.Record){
                this.view.sm.selectRecords(selections);
            }else{
                this.view.sm.selectRows(selections);

            }
        }else{
            this.view.select(selections);
        }
    },

    onSelect: function(){
        var d = this.prepareValue(this.getSelectedRecords());
        var returnValue = d.hv ? d.hv.join(this.valueSeparator) : '';
        var displayValue = d.dv ? d.dv.join(this.displaySeparator) : '';

        Ext.form.ComboBox.superclass.setValue.call(this, displayValue);
        this.hiddenField.setAttribute('value', returnValue);

        if(Ext.QuickTips){ // fix for floating editors interacting with DND
            Ext.QuickTips.enable();
        }
        this.window.hide();
    },

    prepareValue:function(sRec){
        this.el.dom.qtip = '';
        if (sRec.length > 0) {
            var vals = {"hv": [],"dv": []};
            Ext.each(sRec, function(i){
                vals.hv.push(i.get(this.valueField));
                if (this.displayFieldTpl) {
                    vals.dv.push(this.displayFieldTpl.apply(i.data));
                } else {
                    vals.dv.push(i.get(this.displayField));
                }

                if(this.qtipTpl){
                    this.el.dom.qtip += this.qtipTpl.apply(i.data);
                }

            }, this);
            return vals;
        }
        return false;
    },

    getValue:function(){
        var v = this.hiddenField.value;
        if(v === this.emptyText || v === undefined){
            v = '';
        }
        return v;
    },

    onTriggerClick: function(e){
    	if (this.isReadOnly)  //避免使用 readOnly
    	    return
    	    
        // Store Load
        if (!this.isStoreLoaded) {
            this.view.store.load();
            this.isStoreLoaded = true;
        } else if (this.alwaysLoadStore === true) {
            this.view.store.reload();
        }

        this.windowConfig = Ext.apply(this.windowConfig,
        {
            title: this.lovTitle,
            width: this.lovWidth,
            height: this.lovHeight,
            modal : true,
            autoScroll: true,
            layout: 'fit',
            bbar: [{
            	text: '<img src="media/images/divo/delete-red.gif">取消',
            	tooltip : "关闭窗口不做任何选择",
            	handler: function(){
                	this.select(this.selections);
                	this.window.hide();
            	},
            	scope: this
           	},{
           		text: '<img src="media/images/divo/clear.gif">清除', 
            	tooltip : "清除当前所选行",
           		handler: function(){ 
           		    this.clearSelections();
         		},
         		scope: this
        	},'->',{
        		text: '<img src="media/images/divo/add2.gif">选择',
            	tooltip : "关闭窗口并选择所选行",
        		handler: this.onSelect,
        		scope: this
       		}],
            items: this.view
        },{shadow: false, frame: true});

        if(!this.window){
            this.window = new Ext.Window(this.windowConfig);

            if (e.xy)
            	this.window.setPagePosition(e.xy[0] + 16, e.xy[1] + 16);

            this.window.on('beforeclose', function(){
                this.window.hide();
                return false;
            }, this);

            this.window.on('hide', this.validate, this);
            this.view.on('dblclick', this.onSelect, this);
            this.view.on('render', this.initSelect, this);
        }

        this.window.show();
    },
    initSelect: function() {
    	if (!this.hiddenValue) return
    	
        rows = this.hiddenValue.split(''+this.valueSeparator);
        var records = new Array();
        
		for(i=0;i<rows.length;i++) {
			record = this.view.getStore().getById(rows[i]);
			records.push(record);
		} 
		
        this.view.sm.selectRecords(records, true);            
    },
    //public
    setNameValue : function(name,id) {
    	this.setValue(name)
    	this.hiddenField.setAttribute('value', id)
    }
    
});

Ext.reg('xlovfield', Ext.ux.form.LovField);;Ext.namespace("Ext.ux"); 

/** 
 * @class Ext.ux.DDView 
 * A DnD enabled version of Ext.View. 
 * @param {Element/String} container The Element in which to create the View. 
 * @param {String} tpl The template string used to create the markup for each element of the View 
 * @param {Object} config The configuration properties. These include all the config options of 
 * {@link Ext.View} plus some specific to this class.<br /> 
 * <p> 
 * Drag/drop is implemented by adding {@link Ext.data.Record}s to the target DDView. If copying is 
 * not being performed, the original {@link Ext.data.Record} is removed from the source DDView.<br /> 
 * <p> 
 * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code> 
.x-view-drag-insert-above { 
    border-top:1px dotted #3366cc; 
} 
.x-view-drag-insert-below { 
    border-bottom:1px dotted #3366cc; 
} 
</code></pre> 
 *  
 */ 
Ext.ux.DDView = function(config) {
	if (!config.itemSelector) {
		var tpl = config.tpl;
		if (this.classRe.test(tpl)) {
			config.tpl = tpl.replace(this.classRe, 'class=$1x-combo-list-item $2$1');
		}
		else {
			config.tpl = tpl.replace(this.tagRe, '$1 class="x-combo-list-item" $2');
		}
		config.itemSelector = ".x-combo-list-item";
	}
    Ext.ux.DDView.superclass.constructor.call(this, Ext.apply(config, { 
        border: false 
    })); 
}; 

Ext.extend(Ext.ux.DDView, Ext.DataView, { 
/**    @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */ 
/**    @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */ 
/**    @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */ 
/**    @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */ 

	sortDir: 'ASC',

    isFormField: true, 
     
    classRe: /class=(['"])(.*)\1/, 

    tagRe: /(<\w*)(.*?>)/, 

    reset: Ext.emptyFn, 
     
    clearInvalid: Ext.form.Field.prototype.clearInvalid, 

    msgTarget: 'qtip', 

	afterRender: function() {
		Ext.ux.DDView.superclass.afterRender.call(this);
	    if (this.dragGroup) { 
	        this.setDraggable(this.dragGroup.split(",")); 
	    } 
	    if (this.dropGroup) { 
	        this.setDroppable(this.dropGroup.split(",")); 
	    } 
	    if (this.deletable) { 
	        this.setDeletable(); 
	    } 
	    this.isDirtyFlag = false; 
	    this.addEvents( 
	        "drop" 
	    );
	},
     
    validate: function() { 
        return true; 
    }, 
     
    destroy: function() { 
        this.purgeListeners(); 
        this.getEl().removeAllListeners(); 
        this.getEl().remove(); 
        if (this.dragZone) { 
            if (this.dragZone.destroy) { 
                this.dragZone.destroy(); 
            } 
        } 
        if (this.dropZone) { 
            if (this.dropZone.destroy) { 
                this.dropZone.destroy(); 
            } 
        } 
    }, 

/**    Allows this class to be an Ext.form.Field so it can be found using {@link Ext.form.BasicForm#findField}. */ 
    getName: function() { 
        return this.name; 
    }, 

/**    Loads the View from a JSON string representing the Records to put into the Store. */ 
    setValue: function(v) { 
        if (!this.store) { 
            throw "DDView.setValue(). DDView must be constructed with a valid Store"; 
        } 
        var data = {}; 
        data[this.store.reader.meta.root] = v ? [].concat(v) : []; 
        this.store.proxy = new Ext.data.MemoryProxy(data); 
        this.store.load(); 
    }, 

/**    @return {String} a parenthesised list of the ids of the Records in the View. */ 
    getValue: function() { 
        var result = '('; 
        this.store.each(function(rec) { 
            result += rec.id + ','; 
        }); 
        return result.substr(0, result.length - 1) + ')'; 
    }, 
     
    getIds: function() { 
        var i = 0, result = new Array(this.store.getCount()); 
        this.store.each(function(rec) { 
            result[i++] = rec.id; 
        }); 
        return result; 
    }, 
     
    isDirty: function() { 
        return this.isDirtyFlag; 
    }, 

/** 
 *    Part of the Ext.dd.DropZone interface. If no target node is found, the 
 *    whole Element becomes the target, and this causes the drop gesture to append. 
 */ 
    getTargetFromEvent : function(e) { 
        var target = e.getTarget(); 
        while ((target !== null) && (target.parentNode != this.el.dom)) { 
            target = target.parentNode; 
        } 
        if (!target) { 
            target = this.el.dom.lastChild || this.el.dom; 
        } 
        return target; 
    }, 

/** 
 *    Create the drag data which consists of an object which has the property "ddel" as 
 *    the drag proxy element.  
 */ 
    getDragData : function(e) { 
        var target = this.findItemFromChild(e.getTarget()); 
        if(target) { 
            if (!this.isSelected(target)) { 
                delete this.ignoreNextClick; 
                this.onItemClick(target, this.indexOf(target), e); 
                this.ignoreNextClick = true; 
            } 
            var dragData = { 
                sourceView: this, 
                viewNodes: [], 
                records: [], 
                copy: this.copy || (this.allowCopy && e.ctrlKey) 
            }; 
            if (this.getSelectionCount() == 1) { 
                var i = this.getSelectedIndexes()[0]; 
                var n = this.getNode(i); 
                dragData.viewNodes.push(dragData.ddel = n); 
                dragData.records.push(this.store.getAt(i)); 
                dragData.repairXY = Ext.fly(n).getXY(); 
            } else { 
                dragData.ddel = document.createElement('div'); 
                dragData.ddel.className = 'multi-proxy'; 
                this.collectSelection(dragData); 
            } 
            return dragData; 
        } 
        return false; 
    }, 

//    override the default repairXY. 
    getRepairXY : function(e){ 
        return this.dragData.repairXY; 
    }, 

/**    Put the selections into the records and viewNodes Arrays. */ 
    collectSelection: function(data) { 
        data.repairXY = Ext.fly(this.getSelectedNodes()[0]).getXY(); 
        if (this.preserveSelectionOrder === true) { 
            Ext.each(this.getSelectedIndexes(), function(i) { 
                var n = this.getNode(i); 
                var dragNode = n.cloneNode(true); 
                dragNode.id = Ext.id(); 
                data.ddel.appendChild(dragNode); 
                data.records.push(this.store.getAt(i)); 
                data.viewNodes.push(n); 
            }, this); 
        } else { 
            var i = 0; 
            this.store.each(function(rec){ 
                if (this.isSelected(i)) { 
                    var n = this.getNode(i); 
                    var dragNode = n.cloneNode(true); 
                    dragNode.id = Ext.id(); 
                    data.ddel.appendChild(dragNode); 
                    data.records.push(this.store.getAt(i)); 
                    data.viewNodes.push(n); 
                } 
                i++; 
            }, this); 
        } 
    }, 
     
/**    Specify to which ddGroup items in this DDView may be dragged. */ 
    setDraggable: function(ddGroup) { 
        if (ddGroup instanceof Array) { 
            Ext.each(ddGroup, this.setDraggable, this); 
            return; 
        } 
        if (this.dragZone) { 
            this.dragZone.addToGroup(ddGroup); 
        } else { 
            this.dragZone = new Ext.dd.DragZone(this.getEl(), { 
                containerScroll: true, 
                ddGroup: ddGroup 
            }); 
//            Draggability implies selection. DragZone's mousedown selects the element. 
            if (!this.multiSelect) { this.singleSelect = true; } 

//            Wire the DragZone's handlers up to methods in *this* 
            this.dragZone.getDragData = this.getDragData.createDelegate(this); 
            this.dragZone.getRepairXY = this.getRepairXY; 
            this.dragZone.onEndDrag = this.onEndDrag; 
        } 
    }, 

/**    Specify from which ddGroup this DDView accepts drops. */ 
    setDroppable: function(ddGroup) { 
        if (ddGroup instanceof Array) { 
            Ext.each(ddGroup, this.setDroppable, this); 
            return; 
        } 
        if (this.dropZone) { 
            this.dropZone.addToGroup(ddGroup); 
        } else { 
            this.dropZone = new Ext.dd.DropZone(this.getEl(), { 
                owningView: this, 
                containerScroll: true, 
                ddGroup: ddGroup 
            }); 

//            Wire the DropZone's handlers up to methods in *this* 
            this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this); 
            this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this); 
            this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this); 
            this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this); 
            this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this); 
        } 
    }, 

/**    Decide whether to drop above or below a View node. */ 
    getDropPoint : function(e, n, dd){ 
        if (n == this.el.dom) { return "above"; } 
        var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight; 
        var c = t + (b - t) / 2; 
        var y = Ext.lib.Event.getPageY(e); 
        if(y <= c) { 
            return "above"; 
        }else{ 
            return "below"; 
        } 
    }, 
     
    isValidDropPoint: function(pt, n, data) { 
        if (!data.viewNodes || (data.viewNodes.length != 1)) { 
            return true; 
        } 
        var d = data.viewNodes[0]; 
        if (d == n) { 
            return false; 
        } 
        if ((pt == "below") && (n.nextSibling == d)) { 
            return false; 
        } 
        if ((pt == "above") && (n.previousSibling == d)) { 
            return false; 
        } 
        return true; 
    }, 

    onNodeEnter : function(n, dd, e, data){ 
    	if (this.highlightColor && (data.sourceView != this)) {
	    	this.el.highlight(this.highlightColor);
	    }
        return false; 
    }, 
     
    onNodeOver : function(n, dd, e, data){ 
        var dragElClass = this.dropNotAllowed; 
        var pt = this.getDropPoint(e, n, dd); 
        if (this.isValidDropPoint(pt, n, data)) { 
    		if (this.appendOnly || this.sortField) {
    			return "x-tree-drop-ok-below";
    		}

//            set the insert point style on the target node 
            if (pt) { 
                var targetElClass; 
                if (pt == "above"){ 
                    dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above"; 
                    targetElClass = "x-view-drag-insert-above"; 
                } else { 
                    dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below"; 
                    targetElClass = "x-view-drag-insert-below"; 
                } 
                if (this.lastInsertClass != targetElClass){ 
                    Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass); 
                    this.lastInsertClass = targetElClass; 
                } 
            } 
        } 
        return dragElClass; 
    }, 

    onNodeOut : function(n, dd, e, data){ 
        this.removeDropIndicators(n); 
    }, 

    onNodeDrop : function(n, dd, e, data){ 
        if (this.fireEvent("drop", this, n, dd, e, data) === false) { 
            return false; 
        } 
        var pt = this.getDropPoint(e, n, dd); 
        var insertAt = (this.appendOnly || (n == this.el.dom)) ? this.store.getCount() : n.viewIndex; 
        if (pt == "below") { 
            insertAt++; 
        } 

//        Validate if dragging within a DDView 
        if (data.sourceView == this) { 
//            If the first element to be inserted below is the target node, remove it 
            if (pt == "below") { 
                if (data.viewNodes[0] == n) { 
                    data.viewNodes.shift(); 
                } 
            } else { //    If the last element to be inserted above is the target node, remove it 
                if (data.viewNodes[data.viewNodes.length - 1] == n) { 
                    data.viewNodes.pop(); 
                } 
            } 
     
//            Nothing to drop... 
            if (!data.viewNodes.length) { 
                return false; 
            } 

//            If we are moving DOWN, then because a store.remove() takes place first, 
//            the insertAt must be decremented. 
            if (insertAt > this.store.indexOf(data.records[0])) { 
                insertAt--; 
            } 
        } 

//        Dragging from a Tree. Use the Tree's recordFromNode function. 
        if (data.node instanceof Ext.tree.TreeNode) { 
            var r = data.node.getOwnerTree().recordFromNode(data.node); 
            if (r) { 
                data.records = [ r ]; 
            } 
        } 
         
        if (!data.records) { 
            alert("Programming problem. Drag data contained no Records"); 
            return false; 
        } 

        for (var i = 0; i < data.records.length; i++) { 
            var r = data.records[i]; 
            var dup = this.store.getById(r.id); 
            if (dup && (dd != this.dragZone)) { 
				if(!this.allowDup && !this.allowTrash){
                	Ext.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1); 
					return true
				}
				var x=new Ext.data.Record();
				r.id=x.id;
				delete x;
			}
            if (data.copy) { 
                this.store.insert(insertAt++, r.copy()); 
            } else { 
                if (data.sourceView) { 
                    data.sourceView.isDirtyFlag = true; 
                    data.sourceView.store.remove(r); 
                } 
                if(!this.allowTrash)this.store.insert(insertAt++, r); 
            } 
			if(this.sortField){
				this.store.sort(this.sortField, this.sortDir);
			}
            this.isDirtyFlag = true; 
        } 
        this.dragZone.cachedTarget = null; 
        return true; 
    }, 

//    Ensure the multi proxy is removed 
    onEndDrag: function(data, e) { 
        var d = Ext.get(this.dragData.ddel); 
        if (d && d.hasClass("multi-proxy")) { 
            d.remove(); 
            //delete this.dragData.ddel; 
        } 
    }, 

    removeDropIndicators : function(n){ 
        if(n){ 
            Ext.fly(n).removeClass([ 
                "x-view-drag-insert-above", 
				"x-view-drag-insert-left",
				"x-view-drag-insert-right",
                "x-view-drag-insert-below"]); 
            this.lastInsertClass = "_noclass"; 
        } 
    }, 

/** 
 *    Utility method. Add a delete option to the DDView's context menu. 
 *    @param {String} imageUrl The URL of the "delete" icon image. 
 */ 
    setDeletable: function(imageUrl) { 
        if (!this.singleSelect && !this.multiSelect) { 
            this.singleSelect = true; 
        } 
        var c = this.getContextMenu(); 
        this.contextMenu.on("itemclick", function(item) { 
            switch (item.id) { 
                case "delete": 
                    this.remove(this.getSelectedIndexes()); 
                    break; 
            } 
        }, this); 
        this.contextMenu.add({ 
            icon: imageUrl || AU.resolveUrl("/images/delete.gif"), 
            id: "delete", 
            text: AU.getMessage("deleteItem") 
        }); 
    }, 
     
/**    Return the context menu for this DDView. */ 
    getContextMenu: function() { 
        if (!this.contextMenu) { 
//            Create the View's context menu 
            this.contextMenu = new Ext.menu.Menu({ 
                id: this.id + "-contextmenu" 
            }); 
            this.el.on("contextmenu", this.showContextMenu, this); 
        } 
        return this.contextMenu; 
    }, 
     
    disableContextMenu: function() { 
        if (this.contextMenu) { 
            this.el.un("contextmenu", this.showContextMenu, this); 
        } 
    }, 

    showContextMenu: function(e, item) { 
        item = this.findItemFromChild(e.getTarget()); 
        if (item) { 
            e.stopEvent(); 
            this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true); 
            this.contextMenu.showAt(e.getXY()); 
        } 
    }, 

/** 
 *    Remove {@link Ext.data.Record}s at the specified indices. 
 *    @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove. 
 */ 
    remove: function(selectedIndices) { 
        selectedIndices = [].concat(selectedIndices); 
        for (var i = 0; i < selectedIndices.length; i++) { 
            var rec = this.store.getAt(selectedIndices[i]); 
            this.store.remove(rec); 
        } 
    }, 

/** 
 *    Double click fires the event, but also, if this is draggable, and there is only one other 
 *    related DropZone that is in another DDView, it drops the selected node on that DDView. 
 */ 
    onDblClick : function(e){ 
        var item = this.findItemFromChild(e.getTarget()); 
        if(item){ 
            if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) { 
                return false; 
            } 
            if (this.dragGroup) { 
                var targets = Ext.dd.DragDropMgr.getRelated(this.dragZone, true); 

//                Remove instances of this View's DropZone 
                while (targets.contains(this.dropZone)) { 
                    targets.remove(this.dropZone); 
                } 

//                If there's only one other DropZone, and it is owned by a DDView, then drop it in 
                if ((targets.length == 1) && (targets[0].owningView)) { 
                    this.dragZone.cachedTarget = null; 
                    var el = Ext.get(targets[0].getEl()); 
                    var box = el.getBox(true); 
                    targets[0].onNodeDrop(el.dom, { 
                        target: el.dom, 
                        xy: [box.x, box.y + box.height - 1] 
                    }, null, this.getDragData(e)); 
                } 
            } 
        } 
    }, 
     
    onItemClick : function(item, index, e){ 
//        The DragZone's mousedown->getDragData already handled selection 
        if (this.ignoreNextClick) { 
            delete this.ignoreNextClick; 
            return; 
        } 

        if(this.fireEvent("beforeclick", this, index, item, e) === false){ 
            return false; 
        } 
        if(this.multiSelect || this.singleSelect){ 
            if(this.multiSelect && e.shiftKey && this.lastSelection){ 
                this.select(this.getNodes(this.indexOf(this.lastSelection), index), false); 
            } else if (this.isSelected(item) && e.ctrlKey) { 
                this.deselect(item); 
            }else{ 
                this.deselect(item); 
                this.select(item, this.multiSelect && e.ctrlKey); 
                this.lastSelection = item; 
            } 
            e.preventDefault(); 
        } 
        return true; 
    } 
});  
;//version 3.0

Ext.ux.Multiselect = Ext.extend(Ext.form.Field,  {
	store:null,
	dataFields:[],
	data:[],
	width:100,
	height:100,
	displayField:0,
	valueField:1,
	allowBlank:true,
	minLength:0,
	maxLength:Number.MAX_VALUE,
	blankText:Ext.form.TextField.prototype.blankText,
	minLengthText:'Minimum {0} item(s) required',
	maxLengthText:'Maximum {0} item(s) allowed',
	copy:false,
	allowDup:false,
	allowTrash:false,
	legend:null,
	focusClass:undefined,
	delimiter:',',
	view:null,
	dragGroup:null,
	dropGroup:null,
	tbar:null,
	appendOnly:false,
	sortField:null,
	sortDir:'ASC',
	defaultAutoCreate : {tag: "div"},
	
    initComponent: function(){
		Ext.ux.Multiselect.superclass.initComponent.call(this);
		this.addEvents({
			'dblclick' : true,
			'click' : true,
			'change' : true,
			'drop' : true
		});		
	},
    onRender: function(ct, position){
		var fs, cls, tpl;
		Ext.ux.Multiselect.superclass.onRender.call(this, ct, position);

		cls = 'ux-mselect';

		fs = new Ext.form.FieldSet({
			renderTo:this.el,
			title:this.legend,
			height:this.height,
			width:this.width,
			style:"padding:1px;",
			tbar:this.tbar
		});
		//if(!this.legend)fs.el.down('.'+fs.headerCls).remove();  //by fzx
		fs.body.addClass(cls);

		tpl = '<tpl for="."><div class="' + cls + '-item';
		if(Ext.isIE || Ext.isIE7)tpl+='" unselectable=on';
		else tpl+=' x-unselectable"';
		tpl+='>{' + this.displayField + '}</div></tpl>';

		if(!this.store){
			this.store = new Ext.data.SimpleStore({
				fields: this.dataFields,
				data : this.data
			});
		}

		this.view = new Ext.ux.DDView({
			multiSelect: true, store: this.store, selectedClass: cls+"-selected", tpl:tpl,
			allowDup:this.allowDup, copy: this.copy, allowTrash: this.allowTrash, 
			dragGroup: this.dragGroup, dropGroup: this.dropGroup, itemSelector:"."+cls+"-item",
			isFormField:false, applyTo:fs.body, appendOnly:this.appendOnly,
			sortField:this.sortField, sortDir:this.sortDir
		});

		fs.add(this.view);
		
		this.view.on('click', this.onViewClick, this);
		this.view.on('beforeClick', this.onViewBeforeClick, this);
		this.view.on('dblclick', this.onViewDblClick, this);
		this.view.on('drop', function(ddView, n, dd, e, data){
	    	return this.fireEvent("drop", ddView, n, dd, e, data);
		}, this);
		
		this.hiddenName = this.name;
		var hiddenTag={tag: "input", type: "hidden", value: "", name:this.name};
		if (this.isFormField) { 
			this.hiddenField = this.el.createChild(hiddenTag);
		} else {
			this.hiddenField = Ext.get(document.body).createChild(hiddenTag);
		}
		fs.doLayout();
	},
	
	initValue:Ext.emptyFn,
	
	onViewClick: function(vw, index, node, e) {
		var arrayIndex = this.preClickSelections.indexOf(index);
		if (arrayIndex  != -1)
		{
			this.preClickSelections.splice(arrayIndex, 1);
			this.view.clearSelections(true);
			this.view.select(this.preClickSelections);
		}
		this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
		this.hiddenField.dom.value = this.getValue();
		this.fireEvent('click', this, e);
		this.validate();		
	},

	onViewBeforeClick: function(vw, index, node, e) {
		this.preClickSelections = this.view.getSelectedIndexes();
		if (this.disabled) {return false;}
	},

	onViewDblClick : function(vw, index, node, e) {
		return this.fireEvent('dblclick', vw, index, node, e);
	},	
	
	getValue: function(valueField){
		var returnArray = [];
		var selectionsArray = this.view.getSelectedIndexes();
		if (selectionsArray.length == 0) {return '';}
		for (var i=0; i<selectionsArray.length; i++) {
			returnArray.push(this.store.getAt(selectionsArray[i]).get(((valueField != null)? valueField : this.valueField)));
		}
		return returnArray.join(this.delimiter);
	},

	setValue: function(values) {
		var index;
		var selections = [];
		this.view.clearSelections();
		this.hiddenField.dom.value = '';
		
		if (!values || (values == '')) { return; }
		
		if (!(values instanceof Array)) { values = values.split(this.delimiter); }
		for (var i=0; i<values.length; i++) {
			index = this.view.store.indexOf(this.view.store.query(this.valueField, 
				new RegExp('^' + values[i] + '$', "i")).itemAt(0));
			selections.push(index);
		}
		this.view.select(selections);
		this.hiddenField.dom.value = this.getValue();
		this.validate();
	},
	
	reset : function() {
		this.setValue('');
	},
	
	getRawValue: function(valueField) {
        var tmp = this.getValue(valueField);
        if (tmp.length) {
            tmp = tmp.split(this.delimiter);
        }
        else{
            tmp = [];
        }
        return tmp;
    },

    setRawValue: function(values){
        setValue(values);
    },

    validateValue : function(value){
        if (value.length < 1) { // if it has no value
             if (this.allowBlank) {
                 this.clearInvalid();
                 return true;
             } else {
                 this.markInvalid(this.blankText);
                 return false;
             }
        }
        if (value.length < this.minLength) {
            this.markInvalid(String.format(this.minLengthText, this.minLength));
            return false;
        }
        if (value.length > this.maxLength) {
            this.markInvalid(String.format(this.maxLengthText, this.maxLength));
            return false;
        }
        return true;
    }
});

Ext.reg("multiselect", Ext.ux.Multiselect);

Ext.ux.ItemSelector = Ext.extend(Ext.form.Field,  {
	msWidth:200,
	msHeight:300,
	hideNavIcons:false,
	imagePath:"",
	iconUp:"media/images/divo/up2.gif",
	iconDown:"media/images/divo/down2.gif",
	iconLeft:"media/images/divo/left2.gif",
	iconRight:"media/images/divo/right2.gif",
	iconTop:"media/images/divo/top2.gif",
	iconBottom:"media/images/divo/bottom2.gif",
	drawUpIcon:true,
	drawDownIcon:true,
	drawLeftIcon:true,
	drawRightIcon:true,
	drawTopIcon:true,
	drawBotIcon:true,
	fromStore:null,
	toStore:null,
	fromData:null, 
	toData:null,
	displayField:0,
	valueField:1,
	switchToFrom:false,
	allowDup:false,
	focusClass:undefined,
	delimiter:',',
	readOnly:false,
	toLegend:null,
	fromLegend:null,
	toSortField:null,
	fromSortField:null,
	toSortDir:'ASC',
	fromSortDir:'ASC',
	toTBar:null,
	fromTBar:null,
	bodyStyle:null,
	border:false,
	defaultAutoCreate:{tag: "div"},
	
    initComponent: function(){
		Ext.ux.ItemSelector.superclass.initComponent.call(this);
		this.addEvents({
			'rowdblclick' : true,
			'change' : true
		});			
	},

    onRender: function(ct, position){
		Ext.ux.ItemSelector.superclass.onRender.call(this, ct, position);

		this.fromMultiselect = new Ext.ux.Multiselect({
			legend: this.fromLegend,
			delimiter: this.delimiter,
			allowDup: this.allowDup,
			copy: this.allowDup,
			allowTrash: this.allowDup,
			dragGroup: this.readOnly ? null : "drop2-"+this.el.dom.id,
			dropGroup: this.readOnly ? null : "drop2-"+this.el.dom.id+",drop1-"+this.el.dom.id,
			width: this.msWidth,
			height: this.msHeight,
			dataFields: this.dataFields,
			data: this.fromData,
			displayField: this.displayField,
			valueField: this.valueField,
			store: this.fromStore,
			isFormField: false,
			tbar: this.fromTBar,
			appendOnly: true,
			sortField: this.fromSortField,
			sortDir: this.fromSortDir
		});
		this.fromMultiselect.on('dblclick', this.onRowDblClick, this);

		if (!this.sorterOnly) {
			if (!this.toStore) {
				this.toStore = new Ext.data.SimpleStore({
					fields: this.dataFields,
					data : this.toData
				});
			}
			this.toStore.on('add', this.valueChanged, this);
			this.toStore.on('remove', this.valueChanged, this);
			this.toStore.on('load', this.valueChanged, this);

			this.toMultiselect = new Ext.ux.Multiselect({
				legend: this.toLegend,
				delimiter: this.delimiter,
				allowDup: this.allowDup,
				dragGroup: this.readOnly ? null : "drop1-"+this.el.dom.id,
				dropGroup: this.readOnly ? null : "drop2-"+this.el.dom.id+",drop1-"+this.el.dom.id,
				width: this.msWidth,
				height: this.msHeight,
				displayField: this.displayField,
				valueField: this.valueField,
				store: this.toStore,
				isFormField: false,
				tbar: this.toTBar,
				sortField: this.toSortField,
				sortDir: this.toSortDir
			});
			this.toMultiselect.on('dblclick', this.onRowDblClick, this);
		} //by fzx
		
		var p = new Ext.Panel({
			bodyStyle:this.bodyStyle,
			border:this.border,
			layout:"table",
			layoutConfig:{columns:3}
		});
		
		this.onRenderOther(p) //by fzx
		
		p.add(this.switchToFrom ? this.toMultiselect : this.fromMultiselect);
		var icons = new Ext.Panel({header:false});
		p.add(icons);
		if (!this.sorterOnly) //by fzx
			p.add(this.switchToFrom ? this.fromMultiselect : this.toMultiselect);
		p.render(this.el);
		icons.el.down('.'+icons.bwrapCls).remove();

		if (this.imagePath!="" && this.imagePath.charAt(this.imagePath.length-1)!="/")
			this.imagePath+="/";
		this.iconUp = this.imagePath + (this.iconUp || 'up2.gif');
		this.iconDown = this.imagePath + (this.iconDown || 'down2.gif');
		this.iconLeft = this.imagePath + (this.iconLeft || 'left2.gif');
		this.iconRight = this.imagePath + (this.iconRight || 'right2.gif');
		this.iconTop = this.imagePath + (this.iconTop || 'top2.gif');
		this.iconBottom = this.imagePath + (this.iconBottom || 'bottom2.gif');
		var el=icons.getEl();
		if (!this.toSortField) {
			this.toTopIcon = el.createChild({tag:'img', src:this.iconTop, style:{cursor:'pointer', margin:'2px'}});
			el.createChild({tag: 'br'});
			this.upIcon = el.createChild({tag:'img', src:this.iconUp, style:{cursor:'pointer', margin:'2px'}});
			el.createChild({tag: 'br'});
		}
		if (!this.sorterOnly) {
			this.addIcon = el.createChild({tag:'img', src:this.switchToFrom?this.iconLeft:this.iconRight, style:{cursor:'pointer', margin:'2px'}});
			el.createChild({tag: 'br'});
			this.removeIcon = el.createChild({tag:'img', src:this.switchToFrom?this.iconRight:this.iconLeft, style:{cursor:'pointer', margin:'2px'}});
			el.createChild({tag: 'br'});
		} // by fzx
		if (!this.toSortField) {
			this.downIcon = el.createChild({tag:'img', src:this.iconDown, style:{cursor:'pointer', margin:'2px'}});
			el.createChild({tag: 'br'});
			this.toBottomIcon = el.createChild({tag:'img', src:this.iconBottom, style:{cursor:'pointer', margin:'2px'}});
		}
		if (!this.readOnly) {
			if (!this.toSortField) {
				if (!this.sorterOnly) {
					this.toTopIcon.on('click', this.toTop, this);
					this.upIcon.on('click', this.up, this);
					this.downIcon.on('click', this.down, this);
					this.toBottomIcon.on('click', this.toBottom, this);
		        } else {
					this.toTopIcon.on('click', this.toTop2, this);
					this.upIcon.on('click', this.up2, this);
					this.downIcon.on('click', this.down2, this);
					this.toBottomIcon.on('click', this.toBottom2, this);
		        } // by fzx
			}
		    if (!this.sorterOnly) {
				this.addIcon.on('click', this.fromTo, this);
				this.removeIcon.on('click', this.toFrom, this);
			} // by fzx
		}
		if (!this.drawUpIcon || this.hideNavIcons) { this.upIcon.dom.style.display='none'; }
		if (!this.drawDownIcon || this.hideNavIcons) { this.downIcon.dom.style.display='none'; }
		if (!this.sorterOnly) {
			if (!this.drawLeftIcon || this.hideNavIcons) { this.addIcon.dom.style.display='none'; }
			if (!this.drawRightIcon || this.hideNavIcons) { this.removeIcon.dom.style.display='none'; }
        } // by fzx
		if (!this.drawTopIcon || this.hideNavIcons) { this.toTopIcon.dom.style.display='none'; }
		if (!this.drawBotIcon || this.hideNavIcons) { this.toBottomIcon.dom.style.display='none'; }

		var tb = p.body.first();
		this.el.setWidth(p.body.first().getWidth());
		p.body.removeClass();
		
		this.hiddenName = this.name;
		var hiddenTag={tag: "input", type: "hidden", value: "", name:this.name};
		this.hiddenField = this.el.createChild(hiddenTag);
		if (!this.sorterOnly) // by fzx
			this.valueChanged(this.toStore);
	},
	
	initValue:Ext.emptyFn,
	
	toTop : function() {
		var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			selectionsArray.sort();
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			selectionsArray = [];
			for (var i=records.length-1; i>-1; i--) {
				record = records[i];
				this.toMultiselect.view.store.remove(record);
				this.toMultiselect.view.store.insert(0, record);
				selectionsArray.push(((records.length - 1) - i));
			}
		}
		this.toMultiselect.view.refresh();
		this.toMultiselect.view.select(selectionsArray);
	},

	toBottom : function() {
		var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			selectionsArray.sort();
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			selectionsArray = [];
			for (var i=0; i<records.length; i++) {
				record = records[i];
				this.toMultiselect.view.store.remove(record);
				this.toMultiselect.view.store.add(record);
				selectionsArray.push((this.toMultiselect.view.store.getCount()) - (records.length - i));
			}
		}
		this.toMultiselect.view.refresh();
		this.toMultiselect.view.select(selectionsArray);
	},
	
	up : function() {
		var record = null;
		var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
		selectionsArray.sort();
		var newSelectionsArray = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
				if ((selectionsArray[i] - 1) >= 0) {
					this.toMultiselect.view.store.remove(record);
					this.toMultiselect.view.store.insert(selectionsArray[i] - 1, record);
					newSelectionsArray.push(selectionsArray[i] - 1);
				}
			}
			this.toMultiselect.view.refresh();
			this.toMultiselect.view.select(newSelectionsArray);
		}
	},

	down : function() {
		var record = null;
		var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
		selectionsArray.sort();
		selectionsArray.reverse();
		var newSelectionsArray = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
				if ((selectionsArray[i] + 1) < this.toMultiselect.view.store.getCount()) {
					this.toMultiselect.view.store.remove(record);
					this.toMultiselect.view.store.insert(selectionsArray[i] + 1, record);
					newSelectionsArray.push(selectionsArray[i] + 1);
				}
			}
			this.toMultiselect.view.refresh();
			this.toMultiselect.view.select(newSelectionsArray);
		}
	},
	
	fromTo : function() {
		var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			if(!this.allowDup)selectionsArray = [];
			for (var i=0; i<records.length; i++) {
				record = records[i];
				if(this.allowDup){
					var x=new Ext.data.Record();
					record.id=x.id;
					delete x;	
					this.toMultiselect.view.store.add(record);
				}else{
					this.fromMultiselect.view.store.remove(record);
					this.toMultiselect.view.store.add(record);
					selectionsArray.push((this.toMultiselect.view.store.getCount() - 1));
				}
			}
		}
		this.toMultiselect.view.refresh();
		this.fromMultiselect.view.refresh();
		if(this.toSortField)this.toMultiselect.store.sort(this.toSortField, this.toSortDir);
		if(this.allowDup)this.fromMultiselect.view.select(selectionsArray);
		else this.toMultiselect.view.select(selectionsArray);
	},
	
	toFrom : function() {
		var selectionsArray = this.toMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.toMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			selectionsArray = [];
			for (var i=0; i<records.length; i++) {
				record = records[i];
				this.toMultiselect.view.store.remove(record);
				if(!this.allowDup){
					this.fromMultiselect.view.store.add(record);
					selectionsArray.push((this.fromMultiselect.view.store.getCount() - 1));
				}
			}
		}
		this.fromMultiselect.view.refresh();
		this.toMultiselect.view.refresh();
		if(this.fromSortField)this.fromMultiselect.store.sort(this.fromSortField, this.fromSortDir);
		this.fromMultiselect.view.select(selectionsArray);
	},
	
	valueChanged: function(store) {
		var record = null;
		var values = [];
		for (var i=0; i<store.getCount(); i++) {
			record = store.getAt(i);
			values.push(record.get(this.valueField));
		}
		this.hiddenField.dom.value = values.join(this.delimiter);
		this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
	},
	
	getValue : function() {
        if (!this.sorterOnly)		
			return this.hiddenField.dom.value;

		//by fzx    
		var values = [];
		var store = this.fromMultiselect.view.store;    
		for (var i=0; i<store.getCount(); i++) {
			record = store.getAt(i);
			values.push(record.get(this.valueField));
		}
		return values.join(this.delimiter);
	},
	
	onRowDblClick : function(vw, index, node, e) {
		return this.fireEvent('rowdblclick', vw, index, node, e);
	},
	
	reset: function(){
		range = this.toMultiselect.store.getRange();
		this.toMultiselect.store.removeAll();
		if (!this.allowDup) {
			this.fromMultiselect.store.add(range);
			this.fromMultiselect.store.sort(this.displayField,'ASC');
		}
		this.valueChanged(this.toMultiselect.store);
	},

	//added by fzx
	toTop2 : function() {
		var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			selectionsArray.sort();
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			selectionsArray = [];
			for (var i=records.length-1; i>-1; i--) {
				record = records[i];
				this.fromMultiselect.view.store.remove(record);
				this.fromMultiselect.view.store.insert(0, record);
				selectionsArray.push(((records.length - 1) - i));
			}
		}
		this.fromMultiselect.view.refresh();
		this.fromMultiselect.view.select(selectionsArray);
	},

	//added by fzx
	toBottom2 : function() {
		var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
		var records = [];
		if (selectionsArray.length > 0) {
			selectionsArray.sort();
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
				records.push(record);
			}
			selectionsArray = [];
			for (var i=0; i<records.length; i++) {
				record = records[i];
				this.fromMultiselect.view.store.remove(record);
				this.fromMultiselect.view.store.add(record);
				selectionsArray.push((this.fromMultiselect.view.store.getCount()) - (records.length - i));
			}
		}
		this.fromMultiselect.view.refresh();
		this.fromMultiselect.view.select(selectionsArray);
	},
	
	//added by fzx
	up2 : function() {
		var record = null;
		var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
		selectionsArray.sort();
		var newSelectionsArray = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
				if ((selectionsArray[i] - 1) >= 0) {
					this.fromMultiselect.view.store.remove(record);
					this.fromMultiselect.view.store.insert(selectionsArray[i] - 1, record);
					newSelectionsArray.push(selectionsArray[i] - 1);
				}
			}
			this.fromMultiselect.view.refresh();
			this.fromMultiselect.view.select(newSelectionsArray);
		}
	},

	//added by fzx
	down2 : function() {
		var record = null;
		var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();
		selectionsArray.sort();
		selectionsArray.reverse();
		var newSelectionsArray = [];
		if (selectionsArray.length > 0) {
			for (var i=0; i<selectionsArray.length; i++) {
				record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);
				if ((selectionsArray[i] + 1) < this.fromMultiselect.view.store.getCount()) {
					this.fromMultiselect.view.store.remove(record);
					this.fromMultiselect.view.store.insert(selectionsArray[i] + 1, record);
					newSelectionsArray.push(selectionsArray[i] + 1);
				}
			}
			this.fromMultiselect.view.refresh();
			this.fromMultiselect.view.select(newSelectionsArray);
		}
	},

	onRenderOther : Ext.emptyFn

});

Ext.reg("itemselector", Ext.ux.ItemSelector);;//Thanks: http://extjs.com/forum/showthread.php?t=20071
Ext.ux.SearchSelectorField = Ext.extend(Ext.form.TwinTriggerField, {
    validationEvent:false,
    validateOnBlur:false,
    trigger1Class:'x-form-clear-trigger',
    trigger2Class:'x-form-search-trigger',
    hideTrigger1:true,
    width:180,
    //hasSearch : false,
    paramName : 'query',
    initComponent : function(){
        Ext.ux.SearchSelectorField.superclass.initComponent.call(this);
        this.on('specialkey', function(f, e){
            if(e.getKey() == e.ENTER){
                this.onTrigger2Click();
            }
        }, this);
    },
    //清除筛选条件
    onTrigger1Click : function(){
        //if(this.hasSearch){
            var o = {start: 0};
            o[this.paramName] = '';
            this.store.reload({params:o});
            this.el.dom.value = '';
            this.triggers[0].hide();
            //this.hasSearch = false;
        //}
    },
    //搜索
    onTrigger2Click : function(){
        var v = this.getRawValue();
        if(v.length < 1){
            this.onTrigger1Click();
            return;
        }
        var o = {start: 0};
        o[this.paramName] = v;
        this.store.reload({params:o});
        //this.hasSearch = true;
        this.triggers[0].show();
    }
});

Ext.ux.SearchSelector = Ext.extend(Ext.ux.ItemSelector,  {
	
   onRenderOther: function(p){
		p.add(new Ext.ux.SearchSelectorField({
			colspan: 3,
			bodyStyle:'padding:20px',
			store: this.fromStore
		}));
   }

});

Ext.reg("searchselector", Ext.ux.SearchSelector);
//EOP
;//根据下拉框中的内容筛选左侧列表内容

Ext.ux.OptionSelectorField = Ext.extend(Ext.form.ComboBox, {
	store : null,
	fieldLabel : '筛选条件',
	displayField: '',
	valueField: '',
	hiddenName: '',
	emptyText:'请选择...',
	optionFilterField : '',
    initComponent : function(){
        Ext.apply(this,{
			waitMsgTarget: true,
			triggerAction: 'all',
			forceSelection:true,
			typeAhead:true, 
			mode: 'local',
			selectOnFocus:true,
			allowBlank:true        
        })    	
        Ext.ux.OptionSelectorField.superclass.initComponent.call(this);
    }
});

Ext.ux.OptionSelector = Ext.extend(Ext.ux.ItemSelector,  {
   optionStore : null,
   optionFieldLabel: null,
   optionDisplayField: null,
   optionValueField: null,
   optionHiddenName: null,
   optionEmptyText: null,
   optionFilterField: null,
   
   onRenderOther: function(p){
		var fld = new Ext.ux.OptionSelectorField({
			colspan: 3,
			bodyStyle:'padding:20px',
			store: this.optionStore,
			fieldLabel : this.optionFieldLabel || '筛选条件',
			displayField: this.optionDisplayField,
			valueField: this.optionValueField,
			hiddenName: this.optionHiddenName,
			emptyText:this.optionEmptyText || '请选择...',
			itemSelectorId : this.id,
			optionFilterField : this.optionFilterField
		})
		p.add(fld) //TODO: 显示fieldLabel
	
		fld.on('select',this.onOptionSelect,this)
   },
   onOptionSelect : function(combo, value) {  		
		this.fromStore.filter(this.optionFilterField, combo.getValue())
	}

});

Ext.reg("optionselector", Ext.ux.OptionSelector);
//EOP
;/**
 * @author Robert Williams (vtswingkid)
 * @version 1.0.4
 * Thanks: http://extjs.com/forum/showthread.php?t=23250
 */
Ext.ux.RadioGroup = Ext.extend(Ext.form.Field,  {
    /**
     * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
     */
    focusClass : undefined,
    /**
     * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
     */
    fieldClass: "x-form-field",
    /**
     * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
     */
    checked: false,
    /**
     * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
     * {tag: "input", type: "radio", autocomplete: "off"})
     */
    defaultAutoCreate : { tag: "input", type: 'radio', autocomplete: "off"},
    /**
     * @cfg {String} boxLabel The text that appears beside the checkbox
     */
	
	getId:function(){
		//if multiple radios are defined use this information
		if(this.radios && this.radios instanceof Array){
			if(this.radios.length){
				var r=this.radios[0];
				this.value=r.value;
				this.boxLabel=r.boxLabel;
				this.checked=r.checked || false;
				this.readOnly=r.readOnly || false;
				this.disabled=r.disabled || false;
				this.tabIndex=r.tabIndex;
				this.cls=r.cls;
				this.listeners=r.listeners;
				this.style=r.style;
				this.bodyStyle=r.bodyStyle;
				this.hideParent=r.hideParent;
				this.hidden=r.hidden;
			}
		}
		Ext.ux.RadioGroup.superclass.getId.call(this);
	},

	// private
    initComponent : function(){
        Ext.ux.RadioGroup.superclass.initComponent.call(this);
        this.addEvents(
            /**
             * @event change
             * Fires when the radio value changes.
             * @param {Ext.vx.RadioGroup} this This radio
             * @param {Boolean} checked The new checked value
             */
            'check'
        );
    },

    // private
    onResize : function(){
        Ext.ux.RadioGroup.superclass.onResize.apply(this, arguments);
        if(!this.boxLabel){
            this.el.alignTo(this.wrap, 'c-c');
        }
    },
    
    // private
    initEvents : function(){
        Ext.ux.RadioGroup.superclass.initEvents.call(this);
        this.el.on("click", this.onClick,  this);
        this.el.on("change", this.onClick,  this);
    },

	// private
    getResizeEl : function(){
        return this.wrap;
    },

    // private
    getPositionEl : function(){
        return this.wrap;
    },

    /**
     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
     * @method
     */
    markInvalid : Ext.emptyFn,
    /**
     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
     * @method
     */
    clearInvalid : Ext.emptyFn,

    // private
    onRender : function(ct, position){
        Ext.ux.RadioGroup.superclass.onRender.call(this, ct, position);
        this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
        if(this.boxLabel){
            this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
        }
		if(!this.isInGroup){
			this.wrap.applyStyles({'padding-top':'2px'});
		}
        if(this.checked){
            this.setChecked(true);
        }else{
            this.checked = this.el.dom.checked;
        }
		if (this.radios && this.radios instanceof Array) {
			this.els=new Array();
			this.els[0]=this.el;
			for(var i=1;i<this.radios.length;i++){
				var r=this.radios[i];
				this.els[i]=new Ext.ux.RadioGroup({
					renderTo:this.wrap,
					hideLabel:true,
					boxLabel:r.boxLabel,
					checked:r.checked || false,
					value:r.value,
					name:this.name || this.id,
					readOnly:r.readOnly || false,
					disabled:r.disabled || false,
					tabIndex:r.tabIndex,
					cls:r.cls,
					listeners:r.listeners,
					style:r.style,
					bodyStyle:r.bodyStyle,
					hideParent:r.hideParent,
					hidden:r.hidden,
					isInGroup:true
				});
				if (this.horizontal) {
					this.els[i].el.up('div.x-form-check-wrap').applyStyles({
						'display': 'inline',
						'padding-left': '5px'
					});
				}
			}
			if(this.hidden)this.hide();
		}
    },
    
    initValue : function(){
        if(this.value !== undefined){
            this.el.dom.value=this.value;
        }else if(this.el.dom.value.length > 0){
            this.value=this.el.dom.value;
        }
    },
	
    // private
    onDestroy : function(){
		if (this.radios && this.radios instanceof Array) {
			var cnt = this.radios.length;
			for(var x=1;x<cnt;x++){
				this.els[x].destroy();
			}
		}
        if(this.wrap){
            this.wrap.remove();
        }
        Ext.ux.RadioGroup.superclass.onDestroy.call(this);
    },

	setChecked:function(v){
        if(this.el && this.el.dom){
			var fire = false;
			if(v != this.checked)fire=true;
			this.checked=v;
            this.el.dom.checked = this.checked;
            this.el.dom.defaultChecked = this.checked;
    	    if(fire)this.fireEvent("check", this, this.checked);
	    }
    },
    /**
     * Returns the value of the checked radio.
     * @return {Mixed} value
     */
    getValue : function(){
        if(!this.rendered) {
            return this.value;
        }
        var p=this.el.up('form');//restrict to the form if it is in a form
		if(!p)p=Ext.getBody();
		var c=p.child('input[name='+escape(this.el.dom.name)+']:checked', true);
		return (c)?c.value:this.value;
    },

	// private
    onClick : function(){
        if(this.el.dom.checked != this.checked){
			var p = this.el.up('form');
			if (!p) 
				p = Ext.getBody();
			var els = p.select('input[name=' + escape(this.el.dom.name) + ']');
			els.each(function(el){
				if (el.dom.id == this.id) {
					this.setChecked(true);
				}
				else {
					var e = Ext.getCmp(el.dom.id);
					e.setChecked.apply(e, [false]);
				}
			}, this);
        }
    },

    /**
     * Checks the radio box with the matching value
     * @param {Mixed} v
     */

    setValue : function(v){
        if(!this.rendered) {
            this.value=v;
            return;
        }
        var p=this.el.up('form');//restrict to the form if it is in a form
        if(!p)p=Ext.getBody();
        var target = p.child('input[name=' + escape(this.el.dom.name) + '][value=' + v + ']', true);
        if (target) target.checked = true;
    },
	
	clear: function(){
		if (!this.rendered) return;
		var p = this.el.up('form');//restrict to the form if it is in a form
		if (!p) p = Ext.getBody();
		var c = p.child('input[name=' + escape(this.el.dom.name) + ']:checked', true);
		if (c) c.checked = false;
	},
	
	disable: function(){
		if (!this.rendered) return;
		var p = this.el.up('form');//restrict to the form if it is in a form
		if (!p) p = Ext.getBody();
		var els = p.select('input[name=' + escape(this.el.dom.name) + ']');
		els.each(function(el){
			if (el.dom.id == this.id) {
				Ext.ux.RadioGroup.superclass.disable.call(this);
			}
			else {
				var e = Ext.getCmp(el.dom.id);
				Ext.ux.RadioGroup.superclass.disable.call(e);
			}
		}, this);
	},
	
	enable: function(){
		if (!this.rendered) return;
		var p = this.el.up('form');//restrict to the form if it is in a form
		if (!p) p = Ext.getBody();
		var els = p.select('input[name=' + escape(this.el.dom.name) + ']');
		els.each(function(el){
			if (el.dom.id == this.id) {
				Ext.ux.RadioGroup.superclass.enable.call(this);
			}
			else {
				var e = Ext.getCmp(el.dom.id);
				Ext.ux.RadioGroup.superclass.enable.call(e);
			}
		}, this);
	},

	hide: function(){
		if (!this.rendered) return;
		this.wrap.hide();
		this.wrap.parent().parent().hide();
	},
	
	show: function(){
		if (!this.rendered) return;
		this.wrap.show();
		this.wrap.parent().parent().show();
	}
});
Ext.reg('ux-radiogroup', Ext.ux.RadioGroup);
//EOP
;Ext.namespace('Ext.ux');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */
 
/**
 * @class Ext.ux.Wiz
 * @extends Ext.Window
 *
 * A specific {@link Ext.Window} that models a wizard component.
 * A wizard is basically a dialog that guides a user through various steps
 * where he has to fill out form-data. 
 * A {@link Ext.ux.Wiz}-component consists typically of a {@link Ext.ux.Wiz.Header}
 * and window-buttons ({@link Ext.Button}) which are linked to the {@link Ext.ux.Wiz.Card}s
 * which themself represent the forms the user has to fill out.
 *
 * In order to switch between the cards in the wizard, you need the {@link Ext.ux.layout.CardLayout},
 * which will check if an active-item can be hidden, before the requested new item will be set to
 * 'active', i.e. shown. This is needed since the wizard may not allow a card to be hidden, if
 * the input entered by the user was not valid. You can get this custom layout at 
 * {@link http://www.siteartwork.de/cardlayout}.
 *
 * @constructor
 * @param {Object} config The config object 
 */ 
Ext.ux.Wiz = Ext.extend(Ext.Window, {
	
	/**
	 * @cfg {Object} An object containing the messages for the {@link Ext.LoadMask}
	 * covering the card-panel on request, whereas the property identifies the
	 * msg-text to show, and the value is the message text itself. Defaults to
	 <pre><code>
{
    default : '正在保存...'
}	 
	 </code></pre>
	 *
	 * Depending on the contexts the loadMask has to be shown in (using the method 
	 * showLoadMask of this class), the object can be configure to hold
	 * various messages.
<pre><code>
this.loadMaskConfig = {
    default    : 'Saving...',
    validating : 'Please wait, validating input...',
};	 
// loadMask will be shown, displaying the message 'Please wait, validating input...'
this.showLoadMask(true, 'validating');
	 </code></pre>	 
	 */
	loadMaskConfig : {
	    'default' : '正在保存...'
	},
	
    /**
	 * @cfg {Number} height The height of the dialog. Defaults to "400".	
	 */
    height : 400,
    
    /**
	 * @cfg {Number} width The width of the dialog. Defaults to "540".	
	 */
    width : 540,
    
    /**
	 * @cfg {Boolean} closable Wether the dialog is closable. Defaults to "true".	
	 */
	closable : true,
	
	/**
	 * @cfg {Boolean} resizable Wether the dialog is resizable. Defaults to "false".	
	 */
	resizable : false,
	
	/**
	 * @cfg {Boolean} resizable Wether the dialog is modal. Defaults to "true".	
	 */
	modal : true,  	
	
	/**
	 * @cfg {Array} cards A numeric array with the configured {@link Ext.ux.Wiz.Card}s.
	 * The index of the cards in the array represent the order in which they get displayed
	 * in the wizard (i.e. card at index 0 gets displayed in the first step, card at index 1 gets
	 * displayed in the second step and so on).
	 */
	cards : null,
	
	/**
	 * @cfg {String} previousButtonText The text to render the previous-button with.
	 * Defaults to "&lt; Back" (< Back)
	 */
	previousButtonText : '&lt; 上一步',
	
	/**
	 * @cfg {String} nextButtonText The text to render the next-button with.
	 * Defaults to "Next &gt;" (Next >)
	 */
	nextButtonText : '下一步 &gt;',
	
	/**
	 * @cfg {String} cancelButtonText The text to render the cancel-button with.
	 * Defaults to "Cancel"
	 */
	cancelButtonText : '取消',	
	
	/**
	 * @cfg {String} finishButtonText The text to render the next-button with when the last
	 * step of the wizard is reached. Defaults to "Finish"
	 */
	finishButtonText : '结束',	
	
	/**
	 * @cfg {Object} headerConfig A config-object to use with {@link Ext.ux.Wiz.Header}.
	 * If not present, it defaults to an empty object.
	 */
	headerConfig : {},
	
	/**
	 * @cfg {Object} cardPanelConfig A config-object to use with {@link Ext.Panel}, which
	 * represents the card-panel in this dialog.
	 * If not present, it defaults to an empty object
	 */
	cardPanelConfig : {},	
	
	/**
	 * @param {Ext.Button} The window-button for paging to the previous card.
	 * @private
	 */
	previousButton : null,
	
	/**
	 * @param {Ext.Button} The window-button for paging to the next card. When the
	 * last card is reached, the event fired by and the text rendered to this button
	 * will change.
	 * @private
	 */
	nextButton : null,
	
	/**
	 * @param {Ext.Button} The window-button for canceling the wizard. The event
	 * fired by this button will usually close the dialog.
	 * @private
	 */	
	cancelButton : null,
	
	/**
	 * @param {Ex.Panel} The card-panel that holds the various wizard cards 
	 * ({@link Ext.ux.Wiz.Card}). The card-panel itself uses the custom 
	 * {@link Ext.ux.layout.CardLayout}, which needs to be accessible by this class.
	 * You can get it at {@link http://www.siteartwork.de/cardlayout}.
	 * @private
	 */
	cardPanel : null,
	
	/**
	 * @param {Number} currentCard The current {@link Ext.ux.Wiz.Card} displayed.
	 * Defaults to -1.
	 * @private
	 */
	currentCard : -1,
	
	/**
     * @param {Ext.ux.Wiz.Header} The header-panel of the wizard. 
     * @private
     */
    headPanel : null,
    
    /**
     * @param {Number} cardCount Helper for storing the number of cards used
     * by this wizard. Defaults to 0 (inherits "cards.length" later on).
     * @private
     */
    cardCount : 0,

    /**
     * Inits this component with the specified config-properties and automatically
     * creates its components.
     */
	initComponent : function()
	{
	    this.initButtons();
	    this.initPanels();
	    
	    var title = this.title || this.headerConfig.title;
	    title     = title || "";
	    	    
	    Ext.apply(this, {
	        title     : title,
	        layout    : 'border',    
	        cardCount : this.cards.length,
		    buttons   : [
		        this.previousButton,
			    this.nextButton,
			    this.cancelButton
		    ],
		    items : [
		        this.headPanel,
			    this.cardPanel
		    ]  
	    });
	    
	    this.addEvents(
            /**
             * @event cancel
             * Fires after the cancel-button has been clicked.
             * @param {Ext.ux.Wiz} this
             */   
            'cancel',
            /**
             * @event finish
             * Fires after the last card was reached in the wizard and the
             * next/finish-button has been clicked.
             * @param {Ext.ux.Wiz} this
             * @param {Object} data The collected data of the cards, whereas 
             * the index is the id of the card and the specific values
             * are objects with key/value pairs in the form formElementName : value
             */   
            'finish'
	    );
	    
		Ext.ux.Wiz.superclass.initComponent.call(this);
	},
	
// -------- helper
    /**
     * Returns the form-data of all cards in this wizard. The first index is the
     * id of the card in this wizard,
     * and the values are objects containing key/value pairs in the form of
     * fieldName : fieldValue.
     *
     * @return {Array}
     */
    getWizardData : function()
    {
        var formValues = {};
		var cards = this.cards;
		for (var i = 0, len = cards.length; i < len; i++) {
		    if (cards[i].form) {
		        formValues[cards[i].id] = cards[i].form.getObjectValues(false);    
		    } else {
		        formValues[cards[i].id] = {};
		    }
		}
		
		return formValues;
    },
     
	/**
	 * Switches the state of this wizard between disabled/enabled.
	 * A disabled dialog will have a {@link Ext.LoadMask} covering the card-panel
	 * to prevent user input, and the buttons will be rendered disabled/enabled.
	 * If the dialog is clsable, the close-tool will be masked, too.
	 *
	 * @param {Boolean} enabled "false" to prevent user input and mask the elements,
	 * otherwise true.
	 * @param {String} type The type of msg for the {@Ext.LoadMask} covering 
	 * the cardPanel, as defined in the cfg property "loadMaskConfig"
	 */
	switchDialogState : function(enabled, type)
	{
	    this.showLoadMask(!enabled, type);
        
        this.previousButton.setDisabled(!enabled);
        this.nextButton.setDisabled(!enabled);
        this.cancelButton.setDisabled(true);
        
        if (this.closable) {
            var ct = this.tools['close'];
            switch (enabled) {
                case true:
                    this.tools['close'].unmask();			    
                break;
                
                default:
                    this.tools['close'].mask();    
                break;    
            }    
        }
	},
	
    /**
     * Shows the load mask for this wizard. By default, the cardPanel's body
     * will be masked.
     *
     * @param {Boolean} show true to show the load mask, otherwise false.
     * @param {String} type The type of message for the {@Ext.LoadMask} covering 
	 * the cardPanel, as defined in the cfg property "loadMaskConfig"
     */
    showLoadMask : function(show, type)
    {
        if (!type) {
	        type = 'default';
	    }
	    
    	if (show) {
	        if (this.loadMask == null) {
	            this.loadMask = new Ext.LoadMask(this.body);
	        } 
	        this.loadMask.msg = this.loadMaskConfig['type'];
        	this.loadMask.show();
        } else {
        	if (this.loadMask) {
        		this.loadMask.hide();	
        	}
        }
    }, 	


    /**
     * Inits the listener for the various {@link Ext.ux.Wiz.Card}s used
     * by this component.
     */
	initEvents : function()
	{
	    Ext.ux.Wiz.superclass.initEvents.call(this);
	    
		var cards = this.cards;
		
		for (var i = 0, len = cards.length; i < len; i++) {
			cards[i].on('show', this.onCardShow, this);
			cards[i].on('hide', this.onCardHide, this);
			cards[i].on('clientvalidation', this.onClientValidation, this);
		}
	},

    /**
     * Creates the head- and the card-panel.
     * Be sure to have the custom {@link Ext.ux.layout.CardLayout} available
     * in order to make the card-panel work as expected by this component
     * ({@link http://www.siteartwork.de/cardlayout}).
     */
    initPanels : function()
    {
        var cards           = this.cards;
        var cardPanelConfig = this.cardPanelConfig;
        
        Ext.apply(this.headerConfig, {
            steps : cards.length    
        });
        
        this.headPanel = new Ext.ux.Wiz.Header(this.headerConfig);	
        
        Ext.apply(cardPanelConfig, {
            layout : new Ext.ux.layout.CardLayout(),
            items  : cards	
        });
        
        Ext.applyIf(cardPanelConfig, {
			region     : 'center',
			border     : false,
			activeItem : 0
        }); 
        
        this.cardPanel = new Ext.Panel(cardPanelConfig);  
    }, 

    /**
     * Creates the instances for the the window buttons.
     */
    initButtons	: function()
    {
		this.previousButton = new Ext.Button({
			text 	 : this.previousButtonText,
			disabled : true,
			minWidth : 75,
			handler  : this.onPreviousClick,
			scope	 : this
		});
		
		this.nextButton = new Ext.Button({
			text	 : this.nextButtonText,
			minWidth : 75,
			handler  : this.onNextClick,
			scope	 : this
		});
		
		this.cancelButton = new Ext.Button({
			text	 : this.cancelButtonText,
			handler  : this.onCancelClick,
			scope	 : this,
			minWidth : 75		
		});
    },

// -------- listeners	
    /**
     * By default, the card firing this event monitors user input in a frequent
     * interval and fires the 'clientvalidation'-event along with it. This listener
     * will enable/disable the next/finish-button in accordance with it, based upon
     * the parameter isValid. isValid" will be set by the form validation and depends
     * on the validators you are using for the different input-elemnts in your form.
     * If the card does not contain any forms, this listener will never be called by the
     * card itself.
     *
     * @param {Ext.ux.Wiz.Card} The card that triggered the event.
     * @param {Boolean} isValid "true", if the user input was valid, otherwise
     * "false"
     */
	onClientValidation : function(card, isValid)
	{
		if (!isValid) {
			this.nextButton.setDisabled(true);	
		} else {
			this.nextButton.setDisabled(false);	
		}
	},
	
	/**
	 * This will render the "next" button as disabled since the bindHandler's delay
	 * of the next card to show might be lagging on slower systems
	 * 
	 */
	onCardHide : function(card)
	{
	    if (this.cardPanel.layout.activeItem.id === card.id) {
	        this.nextButton.setDisabled(true);	
	    }
	},
	
	
	/**
	 * Listener for the "show" event of the card that gets shown in the card-panel.
	 * Renders the next/previous buttons based on the position of the card in the wizard
	 * and updates the head-panel accordingly.
	 *
	 * @param {Ext.ux.Wiz.Card} The card being shown.
	 */
	onCardShow : function(card)
	{
		var parent = card.ownerCt;
		
		var items = parent.items;
		
		for (var i = 0, len = items.length; i < len; i++) {
			if (items.get(i).id == card.id) {
				break;	
			}	
		}
		
		this.currentCard = i;
		this.headPanel.updateStep(i, card.title);
		
		if (i == len-1) {
			this.nextButton.setText(this.finishButtonText);	
		} else {
			this.nextButton.setText(this.nextButtonText);
		}
		
		if (card.isValid()) 
			this.nextButton.setDisabled(false)
		
		if (i == 0) {
			this.previousButton.setDisabled(true);
		} else {
			this.previousButton.setDisabled(false);	
		}
		
	},
	
	/**
	 * Fires the 'cancel'-event. Closes this dialog if the return value of the 
	 * listeners does not equal to "false".
	 */
	onCancelClick : function()
	{
	    if (this.fireEvent('cancel', this) !== false) {
            this.close();
        }
	},
	
	/**
	 * Fires the 'finish'-event. Closes this dialog if the return value of the 
	 * listeners does not equal to "false".
	 */
	onFinish : function()
	{
	    if (this.fireEvent('finish', this, this.getWizardData()) !== false) {
            this.close();
        }
	},	
	
	/**
	 * Listener for the previous-button. 
	 * Switches to the previous displayed {@link Ext.ux.Wiz.Card}.
	 */
	onPreviousClick : function()
	{
		this.publish("divo.wiz.previous",{sender:this.id,currentCard:this.currentCard})
		if (this.currentCard > 0) {
			this.cardPanel.getLayout().setActiveItem(this.currentCard - 1);	
		}
	},
	
	/**
	 * Listener for the next-button. Switches to the next {@link Ext.ux.Wiz.Card}
	 * if the 'beforehide'-method of it did not return false. The functionality
	 * for this is implemented in {@link Ext.ux.layout.CardLayout}, which is needed
	 * as the layout for the card-panel of this component.
	 */
	onNextClick : function()
	{
		this.publish("divo.wiz.next",{sender:this.id,currentCard:this.currentCard})
		if (this.currentCard == this.cardCount-1) {
			this.onFinish();
		} else {
			var ll = this.cardPanel.getLayout()
			ll.setActiveItem(this.currentCard+1)
			//this.cardPanel.getLayout().setActiveItem(this.currentCard+1);
		}
	}
});;Ext.namespace('Ext.ux.Wiz');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */

/**
 * @class Ext.ux.Wiz.Header
 * @extends Ext.BoxComponent
 *
 * A specific {@link Ext.BoxComponent} that can be used to show the current process in an
 * {@link Ext.ux.Wiz}.
 *
 * An instance of this class is usually being created by {@link Ext.ux.Wiz#initPanels} using the
 * {@link Ext.ux.Wiz#headerConfig}-object. 
 *
 * @private
 * @constructor
 * @param {Object} config The config object 
 */ 
Ext.ux.Wiz.Header = Ext.extend(Ext.BoxComponent, {
  
    /**
     * @cfg {Number} height The height of this component. Defaults to "55".
     */  
    height : 55, 
    
    /**
     * @cfg {String} region The Region of this component. Since a {@link Ext.ux.Wiz} 
     * usually uses a {@link Ext.layout.BorderLayout}, this property defaults to
     * "north". If you want to change this property, you should also change the appropriate
     * css-classes that are used for this component.
     */  
    region : 'north', 
     
    /**
     * @cfg {String} title The title that gets rendered in the head of the component. This
     * should be a text describing the purpose of the wizard.
     */  
    title : 'Wizard',
    
    /**
     * @cfg {Number} steps The overall number of steps the user has to go through
     * to finish the wizard. 
     */
    steps : 0, 

    /**
     * @cfg {String} stepText The text in the header indicating the current process in the wizard.
     * (defaults to "Step {0} of {1}: {2}").
     * {0} is replaced with the index (+1) of the current card, {1} is replaced by the
     * total number of cards in the wizard and {2} is replaced with the title-property of the
     * {@link Ext.ux.Wiz.Card}
     * @type String
     */
    stepText : "第 {0}/{1} 步: {2}",

    /**
     * @cfg {Object} autoEl The element markup used to render this component.
     */
	autoEl : {
		tag : 'div',
		cls		 : 'ext-ux-wiz-Header',
		children : [{
		  	tag		 : 'div',
		  	cls		 : 'ext-ux-wiz-Header-title'
		}, {
			tag  : 'div',
			children : [{
				tag : 'div',
				cls : 'ext-ux-wiz-Header-step'
			}, {
				tag : 'div',
				cls : 'ext-ux-wiz-Header-stepIndicator-container'
			}]
		}]
	},

    /**
     * @param {Ext.Element}
     */
  	titleEl : null,

    /**
     * @param {Ext.Element}
     */
    stepEl  : null,
  	
    /**
     * @param {Ext.Element}
     */
  	imageContainer : null,
  	
    /**
     * @param {Array}
     */  	
  	indicators : null,
  
  	/**
  	 * @param {Ext.Template}
  	 */
  	stepTemplate : null,
  	
  	/**
  	 * @param {Number} lastActiveStep Stores the index of the last active card that
  	 * was shown-
  	 */
  	lastActiveStep : -1,
  
// -------- helper
    /**
     * Gets called by  {@link Ext.ux.Wiz#onCardShow()} and updates the header
     * with the approppriate information, such as the progress of the wizard 
     * (i.e. which card is being shown etc.)
     *
     * @param {Number} currentStep The index of the card currently shown in 
     * the wizard
     * @param {String} title The title-property of the {@link Ext.ux.Wiz.Card}
     *
     * @private
     */
  	updateStep : function(currentStep, title)
  	{
  		var html = this.stepTemplate.apply({
  			0 : currentStep+1, 
  			1 : this.steps, 
  			2 : title
  		});
  		
  		this.stepEl.update(html);
  		
  		if (this.lastActiveStep != -1) {
  			this.indicators[this.lastActiveStep].removeClass('ext-ux-wiz-Header-stepIndicator-active');
  		}
  		
  		this.indicators[currentStep].addClass('ext-ux-wiz-Header-stepIndicator-active');
  		
  		this.lastActiveStep = currentStep;
  	},
  
  
// -------- listener  
    /**
     * Overrides parent implementation to render this component properly.
     */
	onRender : function(ct, position)
	{
		Ext.ux.Wiz.Header.superclass.onRender.call(this, ct, position);
	
		this.indicators   = [];
		this.stepTemplate = new Ext.Template(this.stepText),
		this.stepTemplate.compile();
	
	    var el = this.el.dom.firstChild;
	    var ns = el.nextSibling;
	    
		this.titleEl        = new Ext.Element(el);
		this.stepEl         = new Ext.Element(ns.firstChild);
		this.imageContainer = new Ext.Element(ns.lastChild);
	
		this.titleEl.update(this.title);
		
		var image = null;
		for (var i = 0, len = this.steps; i < len; i++) {
			image = document.createElement('div');
			image.innerHTML = "&#160;";
			image.className = 'ext-ux-wiz-Header-stepIndicator';
			this.indicators[i] = new Ext.Element(image);
			this.imageContainer.appendChild(image);
		} 
	}
});;Ext.namespace('Ext.ux.Wiz');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/wizardcomponent
 */

/**
 * @class Ext.ux.Wiz.Card
 * @extends Ext.FormPanel
 *
 * A specific {@link Ext.FormPanel} that can be used as a card in a 
 * {@link Ext.ux.Wiz}-component. An instance of this card does only work properly
 * if used in a panel that uses a {@see Ext.layout.CardLayout}-layout.
 *
 * @constructor
 * @param {Object} config The config object 
 */ 
Ext.ux.Wiz.Card = Ext.extend(Ext.FormPanel, {
	
	/**
	 * @cfg {Boolean} header "True" to create the header element. Defaults to
	 * "false". See {@link Ext.form.FormPanel#header}
	 */
	header : false,
	
	/**
	 * @cfg {Strting} hideMode Hidemode of this component. Defaults to "offsets".
	 * See {@link Ext.form.FormPanel#hideMode}
	 */
	hideMode : 'display',

    initComponent : function()
    {
        this.addEvents(
        	/**
        	 * @event beforecardhide
        	 * If you want to add additional checks to your card which cannot be easily done
        	 * using default validators of input-fields (or using the monitorValid-config option), 
        	 * add your specific listeners to this event.
        	 * This event gets only fired if the activeItem of the ownerCt-component equals to
        	 * this instance of {@see Ext.ux.Wiz.Card}. This is needed since a card layout usually
        	 * hides it's items right after rendering them, involving the beforehide-event. 
        	 * If those checks would be attached to the normal beforehide-event, the card-layout 
        	 * would never be able to hide this component after rendering it, depending on the 
        	 * listeners return value.
        	 * 
        	 * @param {Ext.ux.Wiz.Card} card The card that triggered the event
        	 */
            'beforecardhide'
        );  
	
	
        Ext.ux.Wiz.Card.superclass.initComponent.call(this);
        
    },
	
// -------- helper
	isValid : function()
	{
		if (this.monitorValid) {
			return this.bindHandler();	
		}
		
		return true;
	},	
	
// -------- overrides	
    /**
     * Overrides parent implementation since we allow to add any element
     * in this component which must not be neccessarily be a form-element.
     * So before a call to "isValid()" is about to be made, this implementation
     * checks first if the specific item sitting in this component has a method "isValid"
     * to prevent errors.
     */
    bindHandler : function()
    {
        if(!this.bound){
            return false; // stops binding
        }
        var valid = true;
        this.form.items.each(function(f){
            if(f.isValid && !f.isValid(true)){
                valid = false;
                return false;
            }
        });
        if(this.buttons){
            for(var i = 0, len = this.buttons.length; i < len; i++){
                var btn = this.buttons[i];
                if(btn.formBind === true && btn.disabled === valid){
                    btn.setDisabled(!valid);
                }
            }
        }
        this.fireEvent('clientvalidation', this, valid);
    },
    
	/**
	 * Overrides parent implementation. This is needed because in case 
	 * this method uses "monitorValid=true", the method "startMonitoring" must
	 * not be called, until the "show"-event of this card fires. 
	 */
	initEvents : function()
	{
		var old = this.monitorValid;
		this.monitorValid = false;
        Ext.ux.Wiz.Card.superclass.initEvents.call(this);
		this.monitorValid = old;
		
		this.on('beforehide',     this.bubbleBeforeHideEvent, this);
		
		this.on('beforecardhide', this.isValid,    this);
	    this.on('show',           this.onCardShow, this);
		this.on('hide',           this.onCardHide, this);
    },

// -------- listener	
    /**
     * Checks wether the beforecardhide-event may be triggered.
     */
    bubbleBeforeHideEvent : function()
    {
        var ly         = this.ownerCt.layout;
        var activeItem = ly.activeItem;
        
        if (activeItem && activeItem.id === this.id) {
            return this.fireEvent('beforecardhide', this);    
        }
        
        return true;
    },

    /**
     * Stops monitoring the form elements in this component when the
     * 'hide'-event gets fired.
     */
	onCardHide : function()
	{
		if (this.monitorValid) {
			this.stopMonitoring();	
		}
	},

    /**
     * Starts monitoring the form elements in this component when the
     * 'show'-event gets fired.
     */
	onCardShow : function()
	{
	    if (this.monitorValid) {
			this.startMonitoring();	
		}
	}
	
});;/**
 * 具有自动保存功能的表单
 */
divo.form.AutoSaveForm = Ext.extend(Ext.form.FormPanel, {
	initFocusFldName : null,
	url : null,
	autoSaveInterval : null,
	enableAutoSave : false, //true表示不要自动保存
	saveRequestMethod : 'PUT',
	initComponent : function() {
		divo.waitFor(this.canInitForm,this.initForm,this)
		divo.form.AutoSaveForm.superclass.initComponent.call(this);
	},
	render : function() {
		divo.form.AutoSaveForm.superclass.render.apply(this, arguments)
		this.loadItem()
	},
	canInitForm : function() {
	    var o = this.getForm()
	    if (o)
	    	var fld = this.getForm().findField(this.initFocusFldName)
		return fld
	},
    initForm : function() {
   		this.loadItem()
    	
		var f = this.getForm().findField(this.initFocusFldName)
		f.focus.defer(100, f)

		this.autoSaving = true
		this.hasChanged()
		this.startMonitoring()
    },
	//自动保存
    startMonitoring : function() {
    	if (!this.enableAutoSave) return
    	
        Ext.TaskMgr.start({
            run : function() {
            	if (!this.autoSaving) return false
            	if (this.hasChanged()) this.save()
            },
            interval : this.autoSaveInterval || 500,
            scope: this
        })
    },
	//判断用户是否输入了数据
	hasChanged : function() {
		if (!this.flds) {
			this.flds = {}
			var items = this.getForm().items
			items.each(function(f)	{
				if (f.name)
             		this.flds[f.name] = {fld:f}
			},this)     
		}
		var hasChanged = false
		for (var k in this.flds) {
			var v = this.flds[k]
			if (v.fld.xtype=="datefield") {
				if (v.oldValue==undefined) 
					v.oldValue = v.fld.getRawValue()
				if (v.oldValue!=v.fld.getRawValue()) {
					hasChanged = true
					break
				}
			} else {
				if (v.oldValue==undefined) 
					v.oldValue = v.fld.getValue()
				if (v.oldValue!=v.fld.getValue()) {
					hasChanged = true
					break
				}
			}
		}
		return hasChanged
	},
	beforeDestroy : function() {
		this.autoSaving = false
		divo.form.AutoSaveForm.superclass.beforeDestroy.apply(this, arguments);
	},
	loadItem : function() {
		if (!this.url) return
		
		Ext.Ajax.request({
			scope : this,
			url : this.url,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success)
					this.getForm().setValues(resp.data)
			},
			failure : function(response, options) {
				this.alert(response.responseText);
			}
		})
	},
	save : function(notAsync) {
		if (!this.url) return
		
		var item = this.getForm().getObjectValues()
		Ext.Ajax.request({
			scope : this,
			url : this.url,
			async : notAsync==undefined?true:notAsync,
			method : this.saveRequestMethod,
			jsonData : item,
			success : function(response, options) {
				//var resp = Ext.decode(response.responseText);
				for (var k in this.flds) {
					delete this.flds[k].oldValue 
				}
				this.afterAutoSave()
			},
			failure : function(response, options) {
				this.alert(respons.responseText)	
			}
		})
	},
	//子类可以重写
	afterAutoSave : Ext.emptyFn
	
})
// EOP

;/**
 * 新建或修改表单（基类）
 */
divo.form.AddOrEditForm = Ext.extend(Ext.form.FormPanel, {
	adding : false,
	initFocusFldName : null,
	initComponent : function() {
		divo.waitFor(this.canInitForm,this.initForm,this)
		divo.form.AddOrEditForm.superclass.initComponent.call(this);
	},
	canInitForm : function() {
	    var o = this.getForm()
	    if (this.initFocusFldName)
		    if (o)
		    	var fld = o.findField(this.initFocusFldName)
			return fld
		return true	
	},
    initForm : function(t) {
    	if (!this.adding) 
    		this.loadItem()
    	
	    if (this.initFocusFldName)
			var f = this.getForm().findField(this.initFocusFldName)
			f.focus.defer(t?t:100, f)
    },
	// public
	clearForm : function() {
		this.getForm().reset()
		this.initForm(1000) //新建成功后发出消息会刷新网格，造成焦点丢失，通过增加延时解决
	},
    //修改时加载数据（子类需要重写）
    loadItem: Ext.emptyFn
})    
    
Ext.reg("divo.form.AddOrEditForm", divo.form.AddOrEditForm)
// EOP

;/**
 * 显示IFRAME
 */
divo.layout.IFrameComponent = Ext.extend(Ext.BoxComponent, {
	onRender : function(ct, position) {
		this.el = ct.createChild( {
			tag : 'iframe',
			id : 'iframe-' + this.id,
			frameBorder : 0,
			src : this.url
		})
	}
})
//EOP
;/**
 * 标签式面板
 */
divo.layout.TabbedPanel = Ext.extend(Ext.TabPanel, {
	closable : true,
	panelId : 'main-tabs',
    tabPosition : 'top',	
    margins : '0 0 0 0',
    notCloseMeHTML : '',
	initComponent : function() {

		Ext.apply(this, {
			id : this.panelId,
			activeTab : 0,
			region : 'center',
			border : false,
			margins : this.margins,
			tabPosition : this.tabPosition,
			enableTabScroll : (this.tabPosition == 'top'),
			plugins : new divo.layout.TabCloseMenu()
		});
		
		this.tabPosition != 'bottom' || Ext.apply(this, {
			items : [{
				title : '<img src="media/images/divo/button.gif">',
				closable : false,
				autoScroll : true,
				id : 'not-close-me',
				html: this.notCloseMeHTML
			}]
		});
		
		divo.layout.TabbedPanel.superclass.initComponent.call(this);
	},
	/**
	 * 打开IFRAME方式的Tab
	 * 
	 * @param {String} tabId 标识
	 * @param {String} title 标题
	 * @param {String} url
	 * @return 打开的Panel对象
	 */
	openTab : function(tabId, title, url) {
		var tab;
		if (!(tab = this.getItem(tabId))) {
			var iframe = new divo.layout.IFrameComponent({
				id : tabId,
				autoScroll : true,
				url : url
			});
			tab = new Ext.Panel({
				id : tabId,
				title : title,
				closable : this.closable,
				autoScroll : true,
				border : false,
				layout : 'fit',
				items : [iframe]
			})
			this.add(tab);
		} else {
			tab.setTitle(title);
		}
		this.setActiveTab(tab);
		tab.doLayout();
		return tab;
	},
	/**
	 * 打开非IFRAME方式的TabPanel,其中的内容已经在另外一个Panel中定义好了
	 * 
	 * @param {String} tabId 标识
	 * @param {String} title 标题
	 * @param {Ext.Panel} innerPanel
	 * @return 打开的Panel对象
	 */
	openNormalTab : function(tabId, title, innerPanel) {
		var tab;
		if (!(tab = this.getItem(tabId))) {
			tab = new Ext.Panel({
				id : tabId,
				title : title,
				closable : this.closable,
				autoScroll : true,
				border : false,
				layout : 'fit',
				items : [innerPanel]
			});
			this.add(tab);
		}
		tab.setTitle(title);
		this.setActiveTab(tab);
		tab.doLayout(); // 没有这句，内嵌网格显示就不正常
		return tab;
	},
	/**
	 * 打开非IFRAME方式的TabPanel,其中的内容将随后动态创建
	 * 
	 * @param {String} tabId 标识
	 * @param {String} title 标题
	 */
	openDynamicTab : function(tabId, title) {
		var tab;
		if (!(tab = this.getItem(tabId))) {
			Ext.DomHelper.append(document.body, {
				tag : 'div',
				id : 'div-' + tabId
			});
			tab = new Ext.Panel({
				id : tabId,
				title : false,
				closable : this.closable,
				autoScroll : true,
				border : true,
				layout : 'fit',
				el : 'div-' + tabId
			});
			tab.render();
			this.add(tab);
		}
		tab.setTitle(title);
		this.setActiveTab(tab);
		tab.doLayout();
		return tab;
	}

})

Ext.reg("divo.layout.TabbedPanel", divo.layout.TabbedPanel)
// EOP
;/**
 * Very simple plugin for adding a close context menu to tabs
 */
divo.layout.TabCloseMenu = function() {
	var tabs, menu, ctxItem;
	this.init = function(tp) {
		tabs = tp;
		tabs.on('contextmenu', onContextMenu);
	}

	function onContextMenu(ts, item, e) {
		if (!menu) { // create context menu on first right click
			menu = new Ext.menu.Menu([ {
				id : tabs.id + '-close',
				text : '关闭',
				handler : function() {
					tabs.remove(ctxItem);
				}
			}, {
				id : tabs.id + '-close-others',
				text : '关闭其他',
				handler : function() {
					tabs.items.each(function(item) {
						if (item.closable && item != ctxItem) {
							tabs.remove(item);
						}
					});
				}
			}]);
		}
		ctxItem = item;
		var items = menu.items;
		items.get(tabs.id + '-close').setDisabled(!item.closable);
		var disableOthers = true;
		tabs.items.each(function() {
			if (this != item && this.closable) {
				disableOthers = false;
				return false;
			}
		});
		items.get(tabs.id + '-close-others').setDisabled(disableOthers);
		menu.showAt(e.getPoint());
	}
};
//EOP

;/**********
*
* http://TDG-i.com TDG innovations, LLC
* 03/15/2008, Jay Garcia, jgarcia@tdg-i.com
* borderLayout extension example displaying 'collapsed titles' 
* Blog Link: http://tdg-i.com/30/how-to-get-titles-in-collapsed-panels-for-border-layout
* Purpose : Adds ability to insert title in collapsed layout panels.  
* Note: You must use layout 'tdgi_border' to use this extension.
* Works with EXT 2.0+
*
* Parameters: collapsedTitle, object || string || boolean
* 
* Example Object: 
	collapsedTitle: {
		//Just like an Ext.DomHelper config object
		element : {
			// Required0
			tag  : 'div',
			// Required
			html : 'NorthPanel Collapsed',
			// Set this if you need to.
			style  : "margin-left:3px; color: #FF0000; font-weight: bold;"
		}
	}

*
* Example String: (Html fragment)
	collapsedTitle: '<div style="myStyle">My Collapsed Title</div>'
*
* Example Boolean: (This allows you to use the original title)
	collapsedTitle: true

*
* License : Free to use, just please don't sell without permission.
* This was made for myself, my customers, and the EXT Community.  
* 
* Waranty : None. I can answer questions via email: jgarcia@tdg-i.com
*
**********/

Ext.namespace('Ext.ux', 'Ext.ux.TDGi');

Ext.ux.TDGi.BorderLayout = function(config) {

	Ext.ux.TDGi.BorderLayout.superclass.constructor.call(this, config, this);
	
	
};

Ext.extend(Ext.ux.TDGi.BorderLayout, Ext.layout.BorderLayout, {
	northTitleAdded  : false,
	southTitleAdded  : false,
	eastTitleAdded  : false,
	westTitleAdded  : false,

	doCollapsedTitle : function (ct) {
		function doHtmlInsert(ct, element) {
			if (ct.region == 'east' || ct.region =='west') {
				if (Ext.isIE6 || Ext.isIE7) {
					if (ct.region == 'east') {
						Ext.get(ct.collapsedEl.dom.firstChild).applyStyles({margin:'3px 3px 5px 3px'});
					}
					else {
						Ext.get(ct.collapsedEl.dom.firstChild).applyStyles({margin:'3px auto 5px 3px'});
					}
				}
				return(Ext.DomHelper.append(ct.collapsedEl, element));
			}
			else {
				return(Ext.DomHelper.insertFirst(ct.collapsedEl, element));
			}			
			
		}
		
		if (ct.collapsedTitle) {
			if (typeof ct.collapsedTitle == 'object') {
				if (typeof ct.collapsedTitle.element == 'object') {
					var element = ct.collapsedTitle.element;
					if (element.style) {
						element.style += 'float: left;';	
					}
					else {
						element.style = 'float: left;';	
					}
					doHtmlInsert(ct, element);
					return(true);
				}
			}
			else if (typeof ct.collapsedTitle == 'string') {
				var element = ct.collapsedTitle;
				doHtmlInsert(ct, element);
				return(true)	
			}
			else if (typeof ct.collapsedTitle == 'boolean' && ct.collapsedTitle == true) {
				if (ct.region == 'east' || ct.region =='west') {
					if (Ext.isIE6 || Ext.isIE7) {
						var element = {
							tag : 'div',
							style : "writing-mode: tb-rl;FONT-FAMILY: 'Arial', 'Helvetica';FONT-SIZE: 13px; ",
							html : ct.title 
						}
					}
					else {
						var y = 0, txt = '';
						for (i = 0; y < ct.title.length ; i ++ ) {
							txt += ct.title.substr(y, 1) + '<br />';
							y++;	
						}
						var element = {
							tag : 'div',
							style : "text-align: center;",
							html : txt 
						}
					}
				}
				else {
					var element = {
						tag : 'div',
						html : ct.title ,
						style : 'float: left;'
					}
				
				}
				doHtmlInsert(ct, element);
				return(true);				
			}
			
		}

	},
	// private
	onLayout : function(ct, target){

		var collapsed;
		if(!this.rendered){
			
			target.position();
			target.addClass('x-border-layout-ct');
			var items = ct.items.items;
			collapsed = [];
			for(var i = 0, len = items.length; i < len; i++) {
				var c = items[i];
				var pos = c.region;
				if(c.collapsed){
					collapsed.push(c);
				}
				c.collapsed = false;
				if(!c.rendered){
					c.cls = c.cls ? c.cls +' x-border-panel' : 'x-border-panel';
					c.render(target, i);
				}
				this[pos] = pos != 'center' && c.split ?
					new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
					new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
				this[pos].render(target, c);
			}
			this.rendered = true;
		}

		var size = target.getViewSize();
		if(size.width < 20 || size.height < 20){ // display none?
			if(collapsed){
				this.restoreCollapsed = collapsed;
			}
			return;
		}else if(this.restoreCollapsed){
			collapsed = this.restoreCollapsed;
			delete this.restoreCollapsed;
		}

		var w = size.width, h = size.height;
		var centerW = w, centerH = h, centerY = 0, centerX = 0;

		var n = this.north, s = this.south, west = this.west, e = this.east, c = this.center;
		if(!c){
			throw 'No center region defined in BorderLayout ' + ct.id;
		}

		if(n && n.isVisible()){
			var b = n.getSize();
			var m = n.getMargins();
			b.width = w - (m.left+m.right);
			b.x = m.left;
			b.y = m.top;
			centerY = b.height + b.y + m.bottom;
			centerH -= centerY;
			n.applyLayout(b);
					
			/**********
			*
			* http://TDG-i.com TDG innovations, LLC
			* 03/15/2008, Jay Garcia, jgarcia@tdg-i.com
			* borderLayout extension example displaying 'collapsed titles' 
			* Blog Link: http://tdg-i.com/30/how-to-get-titles-in-collapsed-panels-for-border-layout
			* Purpose : Adds ability to insert title in collapsed layout panels.
			*
			************/
			if (typeof n.collapsedEl != 'undefined' && n.collapsedTitle && this.northTitleAdded == false) {
				if (this.doCollapsedTitle(n)) {
					this.northTitleAdded = true;
				}
			}                    
		}
		if(s && s.isVisible()){
			var b = s.getSize();
			var m = s.getMargins();
			b.width = w - (m.left+m.right);
			b.x = m.left;
			var totalHeight = (b.height + m.top + m.bottom);
			b.y = h - totalHeight + m.top;
			centerH -= totalHeight;
			s.applyLayout(b);

			/**********
			*
			* http://TDG-i.com TDG innovations, LLC
			* 03/15/2008, Jay Garcia, jgarcia@tdg-i.com
			* borderLayout extension example displaying 'collapsed titles' 
			* Blog Link: http://tdg-i.com/30/how-to-get-titles-in-collapsed-panels-for-border-layout
			* Purpose : Adds ability to insert title in collapsed layout panels.
			*
			************/

			if (typeof s.collapsedEl != 'undefined' && s.collapsedTitle && this.southTitleAdded == false) {
				if (this.doCollapsedTitle(s)) {
					this.southTitleAdded = true;
				}
			}                
		}
		if(west && west.isVisible()){
			var b = west.getSize();
			var m = west.getMargins();
			b.height = centerH - (m.top+m.bottom);
			b.x = m.left;
			b.y = centerY + m.top;
			var totalWidth = (b.width + m.left + m.right);
			centerX += totalWidth;
			centerW -= totalWidth;
			west.applyLayout(b);

			/**********
			*
			* http://TDG-i.com TDG innovations, LLC
			* 03/15/2008, Jay Garcia, jgarcia@tdg-i.com
			* borderLayout extension example displaying 'collapsed titles' 
			* Blog Link: http://tdg-i.com/30/how-to-get-titles-in-collapsed-panels-for-border-layout
			* Purpose : Adds ability to insert title in collapsed layout panels.
			*
			************/
			if (typeof west.collapsedEl != 'undefined' && west.collapsedTitle && this.westTitleAdded == false) {
				if (this.doCollapsedTitle(west)) {
					this.westTitleAdded = true;
				}
			
			}                
			
		}
		if(e && e.isVisible()){
			var b = e.getSize();
			var m = e.getMargins();
			b.height = centerH - (m.top+m.bottom);
			var totalWidth = (b.width + m.left + m.right);
			b.x = w - totalWidth + m.left;
			b.y = centerY + m.top;
			centerW -= totalWidth;
			e.applyLayout(b);
	
			/**********
			*
			* http://TDG-i.com TDG innovations, LLC
			* 03/15/2008, Jay Garcia, jgarcia@tdg-i.com
			* borderLayout extension example displaying 'collapsed titles' 
			* Blog Link: http://tdg-i.com/30/how-to-get-titles-in-collapsed-panels-for-border-layout
			* Purpose : Adds ability to insert title in collapsed layout panels.
			*
			************/
			if (typeof e.collapsedEl != 'undefined' && e.collapsedTitle && this.eastTitleAdded == false) {
				if (this.doCollapsedTitle(e)) {
					this.eastTitleAdded = true;
				}
			}                
			
		}

		var m = c.getMargins();
		var centerBox = {
			x: centerX + m.left,
			y: centerY + m.top,
			width: centerW - (m.left+m.right),
			height: centerH - (m.top+m.bottom)
		};
		c.applyLayout(centerBox);

		if(collapsed){
			for(var i = 0, len = collapsed.length; i < len; i++){
				collapsed[i].collapse(false);
			}
		}

		if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
			target.repaint();
		}
	}
});

Ext.Container.LAYOUTS['tdgi_border'] = Ext.ux.TDGi.BorderLayout;;﻿Ext.namespace('Ext.ux.layout');

/**
 * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3
 *
 * @author Thorsten Suckow-Homberg <ts@siteartwork.de>
 * @url http://www.siteartwork.de/cardlayout
 */
 
/**
 * @class Ext.ux.layout.CardLayout
 * @extends Ext.layout.CardLayout
 *
 * A specific {@link Ext.layout.CardLayout} that only sets the active item
 * if the 'beforehide'-method of the card to hide did not return false (in this case,
 * components usually won't be hidden).
 * The original implementation of {@link Ext.layout.CardLayout} does not take
 * the return value of the 'beforehide'-method into account.
 *
 * @constructor
 * @param {Object} config The config object 
 */ 
Ext.ux.layout.CardLayout = Ext.extend(Ext.layout.CardLayout, {
	
    /**
     * Sets the active (visible) item in the layout.
     * 
     * If the currently visible item is still visible after calling the 'hide()
     * method on it, this implementation assumes that the 'beforehide'-event returned
     * false, thus not the item was not allowed to be hidden. The active item will then
     * equal to the item that was active, before this method was called.
     *
     * @param {String/Number} item The string component id or numeric index of the item to activate
     */
    setActiveItem : function(item)
    {
        item = this.container.getComponent(item);
        if(this.activeItem != item){
            if(this.activeItem){
                this.activeItem.hide();
            }
            // check if the beforehide method allowed to 
            // hide the current item
            if (this.activeItem && !this.activeItem.hidden) {
            	return;	
            }
            
            this.activeItem = item;
            item.show();
            this.layout();
        }
    }	
	
});;// Version: 11. August 2008

// AutoTableFormLayout
// Based on http://extjs.com/forum/showthread.php?t=39342 by mbajema and Animal

Ext.namespace('Ext.ux.layout');

// Make the fieldTpl available to ALL layouts
Ext.override(Ext.layout.ContainerLayout, {
    fieldTpl: (function() {
        var t = new Ext.Template(
            '<div class="x-form-item {5}" tabIndex="-1">',
                '<label for="{0}" style="{2}" class="x-form-item-label">{1}{4}</label>',
                '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
                '</div><div class="{6}"></div>',
            '</div>'
        );
        t.disableFormats = true;
        return t.compile();
    })()
});

Ext.ux.layout.AutoTableFormLayout = Ext.extend(Ext.layout.TableLayout, 
{
	labelSeparator: ':',
	
    setContainer: function(ct) 
    {
        // Creating all subitems, figure out how many columns we need
		this.columns = 1;
        var aCols = new Array();
        for(var i = 0, len = ct.items.length; i < len; i++) 
        {	var mainItem = ct.items.itemAt(i);
        	if (mainItem.fieldLabel == "") mainItem.labelSeparator = "";
        	mainItem.labelWidth = mainItem.labelWidth || ct.labelWidth || 100;
        	mainItem.colspan = mainItem.colspan || 1;
        	
        	var cols = mainItem.colspan;
        	if (mainItem.items != undefined)
       		{	// All subitems of this row
       			for(var j = 0, lensubItems = mainItem.items.length; j < lensubItems; j++) 	
            	{	var subItem = mainItem.items[j];
            		Ext.applyIf(subItem, ct.defaults);
            		subItem = Ext.ComponentMgr.create(subItem, subItem.xtype || ct.defaultType || "textfield");
            		if (subItem.fieldLabel == "") subItem.labelSeparator = "";
            		
            		subItem.labelWidth = subItem.labelWidth || ct.subLabelWidth || 0;
            		mainItem.items[j] = subItem;
           	 		subItem.colspan = subItem.colspan || 1;
           	 		cols += subItem.colspan;
           	 		ct.getForm().add(subItem);
            	}
           	 	this.columns = Math.max(this.columns, cols);
           	 }
        	aCols[i] = cols;
        }
       
        // Set colspan
        for(var i = 0, len = ct.items.length; i < len; i++) 
        {	var mainItem = ct.items.itemAt(i);
        	mainItem.colspan += this.columns-aCols[i];
        }
        
       Ext.layout.FormLayout.prototype.setContainer.apply(this, arguments);
       
       this.currentRow = 0;
       this.currentColumn = 0;
       this.cells = [];
    },

    renderItem : function(c, position, target) 
    {
        if (c && !c.rendered)
		{			
        		
			// Render mainItem
            this.doRender(c, 0, Ext.get(this.getNextCell(c)), false);
         		
         	// Render subitems
         	for (var i = 0; i < this.columns-1; i++) 
         	{	if (c.items != undefined && i < c.items.length)
            	{	// Subitem available: Render it
            		var subItem = c.items[i];
            		this.doRender(subItem, 0, Ext.get(this.getNextCell(subItem)), true);
            	}
            }

         	// Hide row?
         	if (c.hideRow) this.showRow(this.currentRow, false);
        
        }
    },
    
    doRender : function (c, position, target, isChild)
    {	// We need this in order to be able to set the labelWidth for mainitems and subitems differently
    	if(typeof c.labelWidth == 'number')
        {	var pad = 5; // (typeof c.ownerCt.labelPad == 'number' ? c.ownerCt.labelPad : 5); // How to get the container here?
    		this.labelStyle = "width:"+c.labelWidth+"px;";
    		if (isChild) this.labelStyle += "width:"+(c.labelWidth-pad)+"px; padding-left:"+pad+"px;";
            this.elementStyle = "padding-left:"+(c.labelWidth+pad)+'px;';
    	}
    	
    	// Call Render
    	Ext.layout.FormLayout.prototype.renderItem.call(this, c, 0, target);
    },
    
    showRow : function (index, show)
    {	var row = this.getRow(index);
		if (show)
				row.style.display = "";
		else	row.style.display = "none";
    }
    
});

Ext.Container.LAYOUTS['autotableform'] = Ext.ux.layout.AutoTableFormLayout;

// ButtonField 
// based on http://extjs.com/forum/showthread.php?t=6099&page=2 by jgarcia@tdg-i.com

Ext.namespace('Ext.ux.form');

Ext.ux.form.ButtonField = Ext.extend(Ext.form.Field,  
{
	defaultAutoCreate : 
	{ 
		tag: 'div' 
	},
	
	value : '',
	
	onRender: function (ct, position) 
	{
            if(!this.el)
            {
                var cfg = this.getAutoCreate();
                if(!cfg.name)
                {
                    cfg.name = this.name || this.id;
                }
                if(this.inputType)
                {
                    cfg.type = this.inputType;   
				}
                this.el = ct.createChild(cfg, position);
            }

		this.button = new Ext.Button(
		{
			renderTo : this.el,
			text     : this.text,
			iconCls  : this.iconCls || null,
			handler  : this.handler || Ext.emptyFn,
			scope    : this.scope   || this
		})
	},
	getValue : function()
	{	return this.button.text;
	},
	setValue : function(value)
	{	this.button.text = value;
		this.value = value;
	}
});

Ext.ComponentMgr.registerType("formBtn", Ext.ux.form.ButtonField );

// SimpleHtml 
// based on Ext.ux.form.ButtonField

Ext.ux.form.SimpleHtml = Ext.extend(Ext.form.Field,  
{
	defaultAutoCreate : 
	{ 
		tag: 'div' 
	},
	
	value : '',
	
	onRender: function (ct, position) 
	{
            if(!this.el)
            {
                var cfg = this.getAutoCreate();
                if(!cfg.name)
                {
                    cfg.name = this.name || this.id;
                }
                if(this.inputType)
                {
                    cfg.type = this.inputType;   
				}
                this.el = ct.createChild(cfg, position);
            }
           
		this.component = new Ext.Component({
			renderTo : this.el,
			autoEl	 : { html: this.value },
			scope    : this.scope || this,
			style    : "padding-top:2px;"+(this.bodyStyle || "") // Correct the aligning with the label
		})
		
		this.hiddenName = this.name; // Make findField working
			
	},
	getValue : function()
	{	return this.component.autoEl.html;
	},
	setValue : function(value)
	{	this.component.getEl().dom.innerHTML = value;
		this.value = value;
	}
});

Ext.ComponentMgr.registerType("formHtml", Ext.ux.form.SimpleHtml );;/**
 * DivoGrid
 */
divo.grid.DivoGrid = Ext.extend(Ext.grid.GridPanel, {
	fieldDefinitions : null,
	//dwrCall : null,
	paging : true,
	//dwrArgs : null,
	remoteSort : true,
	pagingTb : null,
	comboStore : null,
	//pagingDisplayMsg : "Displaying topics {0} - {1} of {2}",
	displayInfo : true,
	showSize : true,
	autoHide : true,
	pageSize : 20,
	
	//初始化
	initComponent : function() {
		Ext.apply(this, {
			defaultDateFormat : this.getAppContext().getDayMonthYearFormat()
		});
		if (!this.ds) {
			if (this.fieldDefinitions) {
				this.ds = this.buildDataStore(this.fieldDefinitions);
			} else {
				this.ds = new Ext.data.Store({});
			}
		}
		if (!this.cm) {
			if (this.fieldDefinitions) {
				this.cm = this.buildColumnModel(this.fieldDefinitions);
			} else {
				this.cm = new Ext.grid.ColumnModel([{
					header : ""
				}]);
			}
		}
		if (!this.bbar) {
			this.bbar = this.createPagingToolbar(this.pageSize, this);
		}
		divo.grid.DivoGrid.superclass.initComponent.call(this);
		
	},
	setFieldDefinitions : function(meta) {
		var store = this.getStore();
		var ds = this.buildDataStore(meta);
		var cm = this.buildColumnModel(meta);
		this.reconfigure(ds, cm);
		this.getView().userResized = false;
		if (this.pagingTb) {
			this.pagingTb.unbind(store);
			this.pagingTb.bind(ds);
		}
	},
	buildDataStore : function(meta) {
		
//		return new jx.data.DwrStoreWithContourFields({
//			fields : meta,
//			dwrCall : this.dwrCall,
//			dwrArgs : this.dwrArgs,
//			remoteSort : this.remoteSort,
//			paging : this.paging
//		});
//		
//Small helper class to make creating Stores from Array data easier		
//    var ds = new Ext.data.SimpleStore({
//        fields: [
//           {name: 'company'},
//           {name: 'price', type: 'float'},
//           {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
//        ]
//    });
//    ds.loadData(myData);
	  //直接用数组模拟后台数据
	  return new Ext.data.SimpleStore({fields:this.buildRecordType(meta)})
	}, 
	createPagingToolbar : function(pageSize, o) {
		this.pagingTb = new divo.PagingToolbar({
			store : o.ds,
			pageSize : pageSize,
			displayInfo : this.displayInfo,
			displayMsg : this.pagingDisplayMsg,
			emptyMsg : "No topics to display"
		});
		return this.pagingTb;
	},
//        columns: [
//            {id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
//            {header: "Price", width: 75, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
//        ],
	buildColumnModel : function(meta) {
		this.autoExpandColumn = null;
		var columns = [];
		for (var i = 0; i < meta.length; i++) {
			var fld = meta[i];
			if (fld.dataOnly) {
				continue;
			}
			var obj = {};
			obj.dataIndex = fld.field;
			if (fld.sortable == undefined) {
				obj.sortable = true;
			} else {
				obj.sortable = fld.sortable;
			}
			obj.header = fld.label;
			if (fld.width) {
				obj.width = fld.width;
			}
			if (fld.tooltip) {
				obj.tooltip = fld.tooltip;
			}
			var renderer = fld.renderer;
			if (!renderer && fld.dataType) {
				switch (fld.dataType) {
					case "date" :
					case "Date" :
						var fmt = this.defaultDateFormat;
						renderer = Ext.util.Format.dateRenderer(fmt);
						break;
					case "lookup" :
					case "Lookup" :
						renderer = function(value, p, record) {
							return value ? value.name : "";
						};
						break;
					case "user" :
					case "User" :
						renderer = function(value, p, record) {
							return value
									? (value.firstName + " " + value.lastName)
									: "";
						};
						break;
					case "action" :
						renderer = this.createActionRenderer(fld.actions);
						break;
				}
			}
			if (renderer) {
				obj.renderer = renderer;
			}
			if (fld.hidden) {
				obj.hidden = true;
			}
			if (fld.field == "name") {
				obj.width = 200;
				obj.id = fld.field;
			}
			columns.push(obj);
		}
		return new Ext.grid.ColumnModel(columns);
	},
	createActionRenderer : function(actions) {
		return function(value, p, record) {
			var html = "";
			for (var j = 0; j < actions.length; j++) {
				var action = actions[j];
				var link = String
						.format(
								"<a href=\"javascript:void(0);\" action=\"{0}\">{1}</a>&nbsp;&nbsp;",
								action.name, action.display);
				html += link;
			}
			return html;
		};
	},
	refresh : function() {
		this.loadData();
	},
	loadData : function(_225) {
//		if (this.pagingTb) {
//			var cursor = (this.pagingTb.cursor) ? this.pagingTb.cursor : 0;
//			this.getStore().load({
//				arg : _225,
//				params : {
//					start : cursor,
//					limit : this.pagingTb.pageSize
//				}
//			});
//		} else {
//			this.getStore().load({
//				arg : _225,
//				params : {
//					start : 0,
//					limit : 0
//				}
//			});
//		}
	},
//RecordType 举例:	
//        fields: [
//           {name: 'company'},
//           {name: 'price', type: 'float'},
//           {name: 'change', type: 'float'},
//           {name: 'pctChange', type: 'float'},
//           {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
//        ]
	buildRecordType : function(meta) {
		var flds = [];
		for (var i = 0; i < meta.length; i++) {
			var fld = meta[i];
			if (fld.columnOnly) {
				continue;
			}
			var obj = {};
			obj.name = fld.field;
			if (fld.dataType) {
				var type = "string";
				switch (fld.dataType) {
					case "string" :
						type = "string";
						break;
					case "text" :
						type = "string";
						break;
					case "date" :
					case "Date" :
						type = "date";
						break;
					case "lookup" :
					case "Lookup" :
						type = "auto";
						break;
					case "boolean" :
					case "Boolean" :
						type = "boolean";
						break;
					default :
						type = "auto";
						break;
				}
				obj.type = type;
			}
			flds.push(obj);
		}
		return flds;
	}
	
});

// EOP
;/**
 * PagingToolbar
 */
divo.PagingToolbar = function(config) {
	config = Ext.applyIf(config, {
		pageSize : 20,
		displayInfo : true,
		displayMsg : "Displaying {0} - {1} of {2}",
		emptyMsg : "No topics to display"
	});
	divo.PagingToolbar.superclass.constructor.call(this, config);
	this.afterRender();
};
Ext.extend(divo.PagingToolbar, Ext.PagingToolbar, {
	showSize : true,
	autoHide : false,
	maxPageSize : null,
	pageSizeLabel : "Page Size:&nbsp;",
	render : function() {
		divo.PagingToolbar.superclass.render.apply(this, arguments);
		if (this.showSize) {
			var _6b = this.createCombo();
			this.addSeparator();
			this.addText(this.pageSizeLabel);
			this.addField(_6b);
			_6b.setRawValue(this.pageSize);
			_6b.on("select", this.changePageSize, this);
			_6b.on("specialkey", this.handleReturn, this);
		}
	},
	changePageSize : function(_6c) {
		if (_6c.isValid()) {
			var _6d = parseInt(_6c.getRawValue());
			if (_6d && this.maxPageSize && _6d > this.maxPageSize) {
				Ext.MessageBox.alert("Warning",
						"Page size exceeds maximum allowed size: "
								+ this.maxPageSize);
				_6c.setValue(this.pageSize);
			} else {
				if (_6d && this.pageSize != _6d) {
					this.pageSize = _6d;
					this.store.load({
						params : {
							start : 0,
							limit : this.pageSize
						}
					});
				}
			}
		}
	},
	handleReturn : function(_6e, _6f) {
		if (_6f.getKey() == Ext.EventObject.RETURN) {
			this.changePageSize(_6e);
		}
	},
	createCombo : function() {
		var _70 = new Ext.data.SimpleStore({
			fields : ["value"],
			data : [[5], [10], [15], [20], [25], [50], [75], [100]]
		});
		var _71 = new Ext.form.ComboBox({
			store : _70,
			displayField : "value",
			valueField : "value",
			mode : "local",
			triggerAction : "all",
			emptyText : "Select a size...",
			selectOnFocus : true,
			width : 100,
			regex : /^\d+$/
		});
		return _71;
	},
	checkResults : function(ds, r, o) {
		var _75 = this.ds.getTotalCount();
		if (_75 <= this.pageSize) {
			this.getEl().hide();
		} else {
			this.getEl().show();
		}
	},
	mybind : function(ds) {
		ds.on("beforeload", this.beforeLoad, this);
		ds.on("load", this.onLoad, this);
		ds.on("loadexception", this.onLoadError, this);
		this.ds = ds;
	},
	myunbind : function(ds) {
		ds.un("beforeload", this.beforeLoad, this);
		ds.un("load", this.onLoad, this);
		ds.un("loadexception", this.onLoadError, this);
		this.ds = undefined;
	}
});
//EOP
;/**
 * SimpleGrid
 */
divo.grid.SimpleGrid = Ext.extend(Ext.Panel, {
	item : null,
	editable : true,
	//defaultDateFormat : "m/d/Y",
	alternateRows : false,
	fields : [],
	rowAlternateClass : "alt",
	selectedClass : "selected",
	hoveredClass : "hovered",
	singleSelect : true,
	muliSelect : false,
	headerClass : "",
	headerUpClass : "selectedUp",
	headerDownClass : "selectedDown",
	tableClass : "j-grid-table",
	fieldDefinitions : null,
	getFieldFunc : null,
	setFieldFunc : null,
	selectRecord : null,
	sortable : true,
	sortInfo : {
		index : -1,
		direction : 0
	},
	loadingMessage : "加载中...",
	_meta : {
		field : null,
		format : null,
		filterer : null,
		sortable : true,
		sortType : "String",
		dataType : String,
		sortFunction : null,
		filterFunction : null,
		label : "",
		align : "left",
		valign : "top",
		getField : function() {
			return this.field || this.label;
		},
		getType : function() {
			return this.dataType;
		}
	},
	initComponent : function() {
		Ext.apply(this, {
			autoScroll : true,
			emptyText : '',
			defaultDateFormat : this.getAppContext().getDayMonthYearFormat()
		});
		divo.grid.SimpleGrid.superclass.initComponent.call(this);
		if (this.fieldDefinitions) {
			this.setFieldDefinitions(this.fieldDefinitions);
		}
	},
	afterRefresh : function() {
		var sortInfo = this.sortInfo;
		if (this.sortInfo && this.sortInfo.index != -1) {
			var head = this.view.getEl().child("thead");
			if (head) {
				var tr = head.child("tr", true);
			}
			if (tr && sortInfo.index < tr.cells.length) {
				var cell = tr.cells[sortInfo.index];
				cell.className = (sortInfo.direction == 0)
						? this.headerDownClass
						: this.headerUpClass;
			}
		}
	},
	setFieldDefinitions : function(meta) {
		this.sortInfo = {
			index : -1,
			direction : 0
		};
		this.fields = [];
		for (var i = 0; i < meta.length; i++) {
			var fld = Ext.applyIf(meta[i], this._meta);
			this.fields.push(fld);
		}
		this.createTemplate(this.fields);
	},
	createTemplate : function(meta) {
		var buf = "<table class=\"" + this.tableClass + "\">";
		buf += this.createHeader();
		buf += "<tbody><tpl for=\".\">";
		buf += "<tr class=\"j-grid-row {alt}\">";
		for (var i = 0; i < meta.length; i++) {
			var fld = meta[i];
			buf += "<td class=\"j-grid-col\">" + "{" + fld.field + "}"
					+ "</td>";
		}
		buf += "</tr>";
		buf += "</tpl></tbody></table>";
		this.template = new Ext.XTemplate(buf);
	},
	createHeader : function() {
		var tpl = new Ext.XTemplate("<thead><tr><tpl for=\".\">",
				"<td width=\"{width}\">{label}</td>", "</tpl></tr></thead>");
		return tpl.apply(this.fields);
	},
	prepareData : function(data, _26b, _26c) {
		var obj = {};
		var flds = this.fields;
		for (var i = 0; i < flds.length; i++) {
			var fld = flds[i];
			var fldDef = fld.field;
			var v = data[fldDef];
			obj[fldDef] = this.getFieldValue(fld, v, _26c);
		}
		if (_26b % 2 == 1) {
			obj["alt"] = (_26b % 2 == 1) ? "j-grid-row-alt" : "";
		}
		return obj;
	},
	getFieldValue : function(fld, v, _275) {
		var _v = v;
		if (fld.renderer) {
			_v = fld.renderer(v, null, _275);
		} else {
			switch (fld.dataType) {
				case "lookup" :
					if (!v) {
						_v = v;
					} else {
						if (typeof(v) == "object") {
							_v = v ? v.name : "";
						} else {
							if (fld.options) {
								var _277 = this.getLookup(fld.options, v);
								_v = _277 ? _277.name : "";
							}
						}
					}
					break;
				case "date" :
					_v = v ? v.format(fld.format
							|| this.defaultDateFormat) : "";
					break;
				case "action" :
					_v = this.createActionHtml(fld.actions);
					break;
				default :
					break;
			}
		}
		return _v;
	},
	getLookup : function(options, val) {
		var result = null;
		if (val) {
			for (var i = 0; i < options.length; i++) {
				var option = options[i];
				if (option.id == val) {
					result = option;
					break;
				}
			}
		}
		return result;
	},
	createActionHtml : function(actions) {
		var html = "";
		if (this.editable) {
			for (var j = 0; j < actions.length; j++) {
				var action = actions[j]
				if (action.hidden) continue
				
				if (j > 0) {
					html += "&nbsp;|&nbsp;"
				}
				var link = String.format(
					"<a href=\"javascript:void(0);\" title=\"{2}\" class=\"{0}\">{1}</a>",
					"j-action-" + action.name, action.display,action.tooltip)
				html += link
			}
		}
		return html;
	},
	sortDataStore : function() {
		var index = this.sortInfo.index;
		if (index >= 0) {
			var fld = this.fields[index];
			this.dataStore.sort(fld.field, this.sortInfo.direction == 0
					? "ASC"
					: "DESC");
		}
	},
	onSelectRow : function(view, rowAt, item, e) {
		var bodyEl = this.body;
		var record = this.dataStore.getAt(rowAt);
		var _cls = null;
		var row = e.getTarget("tr", bodyEl);
		var cell = e.getTarget("td", bodyEl);
		var link = e.getTarget("a", bodyEl);
		if (link != null) {
			var cls = link.className;
			if (cls.indexOf("j-action-") != -1) {
				_cls = cls.substr(9);
			}
		}
		this.onSelectItem(view, rowAt, record, e, _cls, cell.cellIndex);
	},
	onSort : function(view, e) {
		var headEl;
		if (!this.sortable || !(headEl = e.getTarget("thead"))) {
			return false;
		}
		var cell = e.getTarget("td", headEl);
		var cellIndex = cell.cellIndex;
		var fld = this.fields[cellIndex];
		if (!fld.sortable) {
			return false;
		}
		var row = e.getTarget("tr", headEl);
		var tdEl = row.getElementsByTagName("td");
		for (var i = 0; i < tdEl.length; i++) {
			tdEl[i].className = this.headerClass;
		}
		var sortInfo = this.sortInfo;
		if (cellIndex == sortInfo.index) {
			sortInfo.direction = (sortInfo.direction + 1) % 2;
		} else {
			sortInfo = {
				index : cellIndex,
				direction : 0
			};
		}
		cell.className = (sortInfo.direction == 0)
				? this.headerDownClass
				: this.headerUpClass;
		this.sortInfo = sortInfo;
		this.sortDataStore();
		return true;
	},
	//子类可以重写
	onSelectItem : Ext.emptyFn,
	//加载数据
	loadData : function(data) {
		if (!this.dataStore.proxy && !data) //是divo.data.ArrayStore
		    return
		
		if (this.dataStore.proxy && !this.dataStore.proxy.conn.url) //尚未定义url
		    return
		
		this.mask()
		if (data)
			this.dataStore.loadData(data)
		else
			this.dataStore.load()
	},
	mask : function() {
		this.el.mask(this.loadingMessage);
	},
	unmask : function() {
		this.el.unmask();
	},
	onRenderOther : Ext.emptyFn,
	onRender : function(ct, _2a2) {
		divo.grid.SimpleGrid.superclass.onRender.call(this, ct, _2a2);
		if (this.fields) {
			this.view = new Ext.DataView({
				emptyText : this.emptyText,
				tpl : this.template,
				store : this.dataStore,
				itemSelector : "tr.j-grid-row",
				selectedClass : this.selectedClass,
				prepareData : this.prepareData.createDelegate(this),
				listeners : {
					click : {
						fn : this.onSelectRow,
						scope : this
					},
					containerclick : {
						fn : this.onSort,
						scope : this
					},
					afterRefresh : {
						fn : this.afterRefresh,
						scope : this
					},
					loadexception : {
						fn : this.onLoadException,
						scope : this
					}					
				}
			});
			this.add(this.view);
			this.doLayout();
			this.view.store.on("datachanged", this.unmask, this);
			this.onRenderOther();
		}
	},
	onLoadException : function(loader, node, response) {
		divo.showError(response.responseText)
	},
	//public
	setUrl : function(url) {
		this.dataStore.proxy.conn.url = url
	},
	render : function() {
		divo.grid.SimpleGrid.superclass.render.apply(this, arguments)
		this.dataStore.on("load",this.onAfterLoad, this, true)
		this.showList()
	},
	onAfterLoad : Ext.emptyFn,  //子类需重写
	//public
	showList: function() {
		this.loadData()
	}
	
})
// EOP
;/**
 * SimpleGridDetail
 */
divo.grid.SimpleGridDetail = Ext.extend(divo.grid.SimpleGrid, {
	form : null,
	dialog : null,
	dialogWidth : 500,
	dialogHeight : 300,
	dialogTitle : "Dialog Title",
	listTitle : "List Title",
	addItemLabel : "Add Item",
	editItemLabel : "Edit Item",
	deleteItemLabel : "Delete Item",
	selectedItems : new Array(),
	showToolbar : true,
	buttonList : [],
	initComponent : function() {
		if (this.showToolbar) {
			this.tbar = new Ext.Toolbar();
		}
		divo.grid.SimpleGridDetail.superclass.initComponent.call(this);
	},
	loadList : function() {
		this.onLoadList();
	},
	fillForm : function(data) {
		this.showDialog();
		this.form.setItem(data);
		this.form.setFocus();
	},
	fillSelectedArray : function(_2a5) {
		var _2a6 = true;
		if (this.selectedItems == null) {
			this.selectedItems = new Array();
		}
		if (this.selectedItems.length == 0) {
			this.selectedItems.push(_2a5);
		} else {
			for (var _2a7 = 0; _2a7 < this.selectedItems.length; _2a7++) {
				if (this.selectedItems[_2a7] == _2a5) {
					this.selectedItems.splice(_2a7, 1);
					_2a6 = false;
				}
			}
			if (_2a6) {
				this.selectedItems.push(_2a5);
			}
		}
	},
	addItem : function() {
		this.fillForm({});
	},
	checkBoxItem : function(_2a8) {
		this.fillSelectedArray(_2a8.id);
	},
	editItem : function(record) {
		this.onLoadItem(record.id, this.fillForm.createDelegate(this));
	},
	deleteItem : function(_2aa) {
		if (_2aa) {
			Ext.MessageBox.confirm("确认", "要删除吗?", this.deleteItemConfirm
					.createDelegate(this, [_2aa], 0));
		}
	},
	deleteSelectedItems : function() {
		if (this.selectedItems.length != 0) {
			Ext.MessageBox
					.confirm("确认", "要删除吗?", this.deleteSelectedItemsConfirm
							.createDelegate(this, [], 0));
		} else {
			this.warnNoSelection();
		}
	},
	deleteItemConfirm : function(_2ad, _2ae) {
		if (_2ae == "yes") {
			this.onDeleteItem(_2ad.id, {
				callback : this.deleteItemCallback.createDelegate(this),
				exceptionHandler : this.handleDeleteException
						.createDelegate(this)
			});
		}
	},
	deleteItemCallback : function() {
		this.loadList();
	},
	deleteSelectedItemsConfirm : function(_2af) {
		if (_2af == "yes") {
			this.onDeleteSelectedItems(this.selectedItems, {
				callback : this.saveItemCallback.createDelegate(this),
				exceptionHandler : this.handleDeleteException
						.createDelegate(this)
			});
		}
	},
	cancelEdit : function() {
		this.hideDialog();
	},
	saveItem : function() {
		this.onSaveItem(this.form.getUpdatedItem());
	},
	handleDeleteException : function(msg, ex) {
		Ext.MessageBox.alert("提示","不能被删除, 可能已经使用.");
	},
	handleSaveException : function(msg, ex) {
		var _2b5 = ex.errors;
		if (_2b5) {
			this.form.displayErrorMessages(_2b5);
		} else {
			Ext.MessageBox.alert("错误", "保存失败");
		}
	},
	warnNoSelection : function() {
		Ext.MessageBox.alert("提示", "请先选择某行");
	},
	saveItemCallback : function() {
		this.cancelEdit();
		this.loadList();
		//this.fireEvent("aftersave", this);
	},
	//子类应重写此方法
	onLoadItem : Ext.emptyFn,
	//子类应重写此方法
	onSaveItem : Ext.emptyFn,
	//子类应重写此方法
	onDeleteItem : Ext.emptyFn,
	onDeleteSelectedItems : function(ids, _2bd) {
		_2bd.callback();
	},
	onLoadList : function() {
		this.loadData();
	},
	onSelectItem : function(view, rowIndex, record, e, action) {
		switch (action) {
			case "edit" :
				this.editItem(record);
				break;
			case "delete" :
				this.deleteItem(record);
				break;
			case "checkbox" :
				this.checkBoxItem(record);
				break;
			default :
				break;
		}
	},
	showDialog : function() {
		this.dialog.show();
	},
	hideDialog : function() {
		this.dialog.hide();
	},
	onRenderOther : function() {
		var tb = this.getTopToolbar();
		this.titleItem = tb.addText(this.listTitle);
		tb.addFill();
		this.buttonList.push(tb.addButton({
			tooltip : "Add Item",
			text : this.addItemLabel,
			icon : divo.iconAdd, 
			cls : "x-btn-text-icon",
			handler : this.addItem.createDelegate(this)
		}));
		this.createDialog();
	},
	setEditable : function(flag) {
		this.editable = flag;
		for (var i = 0, len = this.buttonList.length; i < len; i++) {
			this.buttonList[i][flag ? "show" : "hide"]();
		}
	},
	setListTitle : function(_2c6) {
		this.listTitle = _2c6;
		if (this.titleItem) {
			this.titleItem.getEl().innerHTML = _2c6;
		}
	},
	createDialog : function() {
		this.dialog = new Ext.Window({
			stateful : true,
			stateId : this.myId('detail-form'),
			title : this.dialogTitle,
			modal : true,
			closable : true,
			closeAction : "hide",
			width : this.dialogWidth,
			height : this.dialogHeight,
			plain : true,
			layout : "fit",
			items : {
				margins : "3 3 3 0",
				autoScroll : true,
				items : this.form ? this.form : {}
			},
			buttons : [{
				text : "确定",
				handler : this.saveItem,
				scope : this
			}, {
				text : "取消",
				handler : this.cancelEdit,
				scope : this
			}]
		});
		this.dialog.render(Ext.getBody());
		if (this.form) {
			this.form.setItem({});
		}
		this.dialog.hide();
	},
	hideDialog : function() {
		this.dialog.hide();
	},
	toggleButtons : function(_2c7) {
		if (this.rendered) {
			this.getTopToolbar().showButtons(_2c7);
		}
	},
	onRender : function(ct, _2c9) {
		divo.grid.SimpleGridDetail.superclass.onRender.call(this, ct, _2c9);
	},
	beforeDestroy : function() {
		if (this.dialog) {
			Ext.destroy(this.dialog);
		}
		divo.grid.SimpleGridDetail.superclass.beforeDestroy
				.apply(this, arguments);
	}
});
//EOP
;/**
 * Thanks: http://extjs.com/forum/showthread.php?t=16811
 *
 * Refresh a grid every x minutes
 */
divo.grid.AutoRefresher = function(config) {
	Ext.apply(this, config)
}
Ext.extend(divo.grid.AutoRefresher, Ext.util.Observable, {
	/**
	 * @cfg {Array} intervals List of refresh intervals (as minutes) to populate
	 *      the comboBox with
	 */
	intervals : [[1], [5], [10], [15], [30], [60]],
	/**
	 * @cfg {String} beforeText Text to display before the comboBox
	 */
	beforeText : '刷新间隔',
	/**
	 * @cfg {String} afterText Text to display after the comboBox
	 */
	afterText : '分',
	/**
	 * @cfg {Mixed} addBefore Toolbar item(s) to add before the PageSizer
	 */
	addBefore : '-',
	/**
	 * @cfg {Mixed} addAfter Toolbar item(s) to be added after the PageSizer
	 */
	addAfter : null,
	init : function(Toolbar) {
		this.Toolbar = Toolbar

		if (Toolbar.store) {
			this.store = Toolbar.store
		}

		Toolbar.on("render", this.onRender, this)

		this.store.startAutoRefresh = function(interval) {
			if (this.autoRefreshProcId) {
				clearInterval(this.autoRefreshProcId)
			}
			this.autoRefreshProcId = setInterval(this.reload.createDelegate(
					this, [{}]), interval * 1000)
		}
		this.store.stopAutoRefresh = function() {
			if (this.autoRefreshProcId) {
				clearInterval(this.autoRefreshProcId)
			}
		}
	},
	update : function(c) {
		var value = c.getValue()
		if (value > 0) {
			this.store.startAutoRefresh(value * 60)
		} else {
			this.store.stopAutoRefresh()
		}
		divo.saveProfileAsync({
			userId : divo.getUserId(),
			msgCode : this.gridId+'-refresher',
			msgValue : value
		})
	},
	onRender : function() {
		this.intervals.unshift(['']) // add a 'never' value
		var config = {
			maskRe : /^d*$/,
			store : new Ext.data.SimpleStore( {
				fields : ['autoRefresh'],
				data : this.intervals
			}),
			displayField : 'autoRefresh',
			typeAhead : true,
			mode : 'local',
			emptyText : 'never',
			triggerAction : 'all',
			selectOnFocus : true,
			width : 50
		}
		Ext.apply(config, this.comboCfg)
		var combo = new Ext.form.ComboBox(config)
		combo.on("change", this.update, this)
		combo.on("select", this.update, this)
		combo.setValue(this.getInterval())

		if (this.addBefore) {
			this.Toolbar.add(this.addBefore)
		}
		this.Toolbar.add(this.beforeText, combo, this.afterText)
		if (this.addAfter) {
			this.Toolbar.add(this.addAfter)
		}
		combo.getEl().on('keydown', function(e) {
			var key = e.getKey()
			switch (key) {
				case Ext.EventObject.ENTER :
					this.update(combo)
			}
		}, this)
	},
	getInterval : function() {
		var result
		divo.restoreProfile(function(retValue) {
		   result = retValue
		},divo.getUserId(), this.gridId+'-refresher')
		
        return result?parseInt(result.msgValue):1
	}

})

// EOP
;//
// Thanks: grid with locking column [Update 3] extjs forum
//
Ext.grid.LockingGridPanel = Ext.extend(Ext.grid.GridPanel, {
	getView : function() {
		if (!this.view) {
			this.view = new Ext.grid.LockingGridView(this.viewConfig);
		}
		return this.view;
	},
	render : function(id){
		Ext.grid.LockingGridPanel.superclass.render.call(this, id);
		this.getView().refresh(true);
	},	
	initComponent : function() {
			if (!this.cm) {
			this.cm = new Ext.grid.LockingColumnModel(this.columns);
			delete this.columns;
		}
		Ext.grid.LockingGridPanel.superclass.initComponent.call(this);
	}
});

Ext.grid.LockingEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, {
	getView : function() {
		if (!this.view) {
			this.view = new Ext.grid.LockingGridView(this.viewConfig);
		}
		return this.view;
	},
	render : function(id){
		Ext.grid.LockingEditorGridPanel.superclass.render.call(this, id);
		this.getView().refresh(true);
	},	
	initComponent : function() {
		if (!this.cm) {
			this.cm = new Ext.grid.LockingColumnModel(this.columns);
			delete this.columns;
		}
		Ext.grid.LockingEditorGridPanel.superclass.initComponent.call(this);
	}
});

Ext.grid.LockingGridView = function(config) {
	Ext.apply(this, config);
	if (!this.templates)
		this.templates = {};
	if (!this.templates.master) {
		this.templates.master = new Ext.Template(
				'<div class="x-grid3" hidefocus="true">',
				'<div class="x-grid3-locked">',
				'<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{lockedHeader}</div></div><div class="x-clear"></div></div>',
				'<div class="x-grid3-scroller"><div class="x-grid3-body">{lockedBody}</div></div>',
				'</div>',
				'<div class="x-grid3-viewport">',
				'<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{header}</div></div><div class="x-clear"></div></div>',
				'<div class="x-grid3-scroller"><div class="x-grid3-body">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
				"</div>", '<div class="x-grid3-resize-marker">&#160;</div>',
				'<div class="x-grid3-resize-proxy">&#160;</div>', "</div>");
	}
	Ext.grid.LockingGridView.superclass.constructor.call(this);
};

Ext.extend(Ext.grid.LockingGridView, Ext.grid.GridView, {

	lockText : "Lock",
	unlockText : "Unlock",

	// Template has changed and we need a few other pointers to keep track
	initElements : function() {
		var E = Ext.Element;

		var el = this.grid.getGridEl();
		el = el.dom.firstChild;// .dom.childNodes[1];
		var cs = el.childNodes;

		this.el = new E(el);

		this.lockedWrap = new E(cs[0]);
		this.lockedHd = new E(this.lockedWrap.dom.firstChild);
		this.lockedInnerHd = this.lockedHd.dom.firstChild;
		this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);
		this.lockedBody = new E(this.lockedScroller.dom.firstChild);

		this.mainWrap = new E(cs[1]);
		this.mainHd = new E(this.mainWrap.dom.firstChild);
		this.innerHd = this.mainHd.dom.firstChild;
		this.scroller = new E(this.mainWrap.dom.childNodes[1]);
		if (this.forceFit) {
			this.scroller.setStyle('overflow-x', 'hidden');
		}
		this.mainBody = new E(this.scroller.dom.firstChild);

		this.focusEl = new E(this.scroller.dom.childNodes[1]);
		this.focusEl.swallowEvent("click", true);

		this.resizeMarker = new E(cs[2]);
		this.resizeProxy = new E(cs[3]);
	},

	getLockedRows : function() {
		return this.hasRows() ? this.lockedBody.dom.childNodes : [];
	},

	getLockedRow : function(row) {
		return this.getLockedRows()[row];
	},

	getCell : function(rowIndex, colIndex) {
		var locked = this.cm.getLockedCount();
		var row;
		if (colIndex < locked) {
			row = this.getLockedRow(rowIndex);
		} else {
			row = this.getRow(rowIndex);
			colIndex -= locked;
		}
		return row.getElementsByTagName('td')[colIndex];
	},

	getHeaderCell : function(index) {
		var locked = this.cm.getLockedCount();
		if (index < locked) {
			return this.lockedHd.dom.getElementsByTagName('td')[index];
		} else {
			return this.mainHd.dom.getElementsByTagName('td')[(index - locked)];
		}
	},

	scrollToTop : function() {
		Ext.grid.LockingGridView.superclass.scrollToTop.call(this);
		this.syncScroll();
	},

	syncScroll : function(e) {
		Ext.grid.LockingGridView.superclass.syncScroll.call(this, e);
		var mb = this.scroller.dom;
		this.lockedScroller.dom.scrollTop = mb.scrollTop;
	},

	processRows : function(startRow, skipStripe) {
		if (this.ds.getCount() < 1) {
			return;
		}
		skipStripe = skipStripe || !this.grid.stripeRows;
		startRow = startRow || 0;
		var cls = ' x-grid3-row-alt ';
		var rows = this.getRows();
		var lrows = this.getLockedRows();
		for (var i = startRow, len = rows.length; i < len; i++) {
			var row = rows[i];
			var lrow = lrows[i];
			row.rowIndex = i;
			lrow.rowIndex = i;
			if (!skipStripe) {
				var isAlt = ((i + 1) % 2 == 0);
				var hasAlt = (' ' + row.className + ' ').indexOf(cls) != -1;
				if (isAlt == hasAlt) {
					continue;
				}
				if (isAlt) {
					row.className += " x-grid3-row-alt";
					lrow.className += " x-grid3-row-alt";
				} else {
					row.className = row.className
							.replace("x-grid3-row-alt", "");
					lrow.className = lrow.className.replace("x-grid3-row-alt",
							"");
				}
			}
		}
	},

	updateSortIcon : function(col, dir) {
		var sc = this.sortClasses;
		var clen = this.cm.getColumnCount();
		var lclen = this.cm.getLockedCount();
		var hds = this.mainHd.select('td').removeClass(sc);
		var lhds = this.lockedHd.select('td').removeClass(sc);
		if (lclen > 0 && col < lclen)
			lhds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
		else
			hds.item(col - lclen).addClass(sc[dir == "DESC" ? 1 : 0]);
	},

	updateAllColumnWidths : function() {
		var tw = this.cm.getTotalWidth();
		var lw = this.cm.getTotalLockedWidth();
		var clen = this.cm.getColumnCount();
		var lclen = this.cm.getLockedCount();
		var ws = [];
		for (var i = 0; i < clen; i++) {
			ws[i] = this.getColumnWidth(i);
		}

		this.innerHd.firstChild.firstChild.style.width = tw - lw;
		this.mainWrap.dom.style.left = lw;
		this.lockedInnerHd.firstChild.firstChild.style.width = lw;

		for (var i = 0; i < clen; i++) {
			var hd = this.getHeaderCell(i);
			hd.style.width = ws[i];
		}

		var ns = this.getRows();
		var lns = this.getLockedRows();
		for (var i = 0, len = ns.length; i < len; i++) {
			ns[i].style.width = tw - lw;
			ns[i].firstChild.style.width = tw - lw;
			lns[i].style.width = lw;
			lns[i].firstChild.style.width = lw;
			for (var j = 0; j < lclen; j++) {
				var row = lns[i].firstChild.rows[0];
				row.childNodes[j].style.width = ws[j];
			}
			for (var j = lclen; j < clen; j++) {
				var row = ns[i].firstChild.rows[0];
				row.childNodes[j].style.width = ws[j];
			}
		}

		this.onAllColumnWidthsUpdated(ws, tw);
	},

	updateColumnWidth : function(col, width) {
		var w = this.cm.getColumnWidth(col);
		var tw = this.cm.getTotalWidth();
		var lclen = this.cm.getLockedCount();
		var lw = this.cm.getTotalLockedWidth();

		var hd = this.getHeaderCell(col);
		hd.style.width = w;

		var ns, gw;
		if (col < lclen) {
			ns = this.getLockedRows();
			gw = lw;
			this.lockedInnerHd.firstChild.firstChild.style.width = gw;
			this.mainWrap.dom.style.left = this.cm.getTotalLockedWidth();
		} else {
			ns = this.getRows();
			gw = tw - lw;
			col -= lclen;
			this.innerHd.firstChild.firstChild.style.width = gw;
		}

		for (var i = 0, len = ns.length; i < len; i++) {
			ns[i].style.width = gw;
			ns[i].firstChild.style.width = gw;
			ns[i].firstChild.rows[0].childNodes[col].style.width = w;
		}

		this.onColumnWidthUpdated(col, w, tw);
		this.layout();
	},

	updateColumnHidden : function(col, hidden) {
		var tw = this.cm.getTotalWidth();
		var lw = this.cm.getTotalLockedWidth();
		var lclen = this.cm.getLockedCount();

		this.innerHd.firstChild.firstChild.style.width = tw;

		var display = hidden ? 'none' : '';

		var hd = this.getHeaderCell(col);
		hd.style.display = display;

		var ns, gw;
		if (col < lclen) {
			ns = this.getLockedRows();
			gw = lw;
			this.lockedHd.dom.firstChild.firstChild.style.width = gw;
			this.mainWrap.dom.style.left = this.cm.getTotalLockedWidth();
		} else {
			ns = this.getRows();
			gw = tw - lw;
			col -= lclen;
			this.innerHd.firstChild.firstChild.style.width = gw;
		}

		for (var i = 0, len = ns.length; i < len; i++) {
			ns[i].style.width = gw;
			ns[i].firstChild.style.width = gw;
			ns[i].firstChild.rows[0].childNodes[col].style.display = display;
		}

		this.onColumnHiddenUpdated(col, hidden, tw);

		delete this.lastViewWidth;
		this.layout();
	},

	doRender : function(cs, rs, ds, startRow, colCount, stripe) {
		var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount - 1;
		var tw = this.cm.getTotalWidth();
		var lw = this.cm.getTotalLockedWidth();
		var clen = this.cm.getColumnCount();
		var lclen = this.cm.getLockedCount();
		var tstyle = 'width:' + this.getTotalWidth() + ';';
		var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {
			tstyle : tstyle
		}, r;
		for (var j = 0, len = rs.length; j < len; j++) {
			r = rs[j];
			cb = [];
			lcb = [];
			var rowIndex = (j + startRow);
			for (var i = 0; i < colCount; i++) {
				c = cs[i];
				p.id = c.id;
				p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last
						? 'x-grid3-cell-last '
						: '');
				p.attr = p.cellAttr = "";
				p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
				p.style = c.style;
				if (p.value == undefined || p.value === "")
					p.value = "&#160;";
				if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
					p.css += ' x-grid3-dirty-cell';
				}
				if (c.locked)
					lcb[lcb.length] = ct.apply(p);
				else
					cb[cb.length] = ct.apply(p);
			}
			var alt = [];
			if (stripe && ((rowIndex + 1) % 2 == 0)) {
				alt[0] = "x-grid3-row-alt";
			}
			if (r.dirty) {
				alt[1] = " x-grid3-dirty-row";
			}
			rp.cols = colCount;
			if (this.getRowClass) {
				alt[2] = this.getRowClass(r, rowIndex, rp, ds);
			}

			rp.alt = alt.join(" ");
			rp.cells = lcb.join("");
			rp.tstyle = 'width:' + lw + ';';
			lbuf[lbuf.length] = rt.apply(rp);

			rp.cells = cb.join("");
			rp.tstyle = 'width:' + (tw - lw) + ';';
			buf[buf.length] = rt.apply(rp);
		}
		return [buf.join(""), lbuf.join("")];
	},

	renderUI : function() {

		var header = this.renderHeaders();
		var body = this.templates.body.apply({
			rows : ''
		});

		var html = this.templates.master.apply({
			body : body,
			header : header[0],
			lockedBody : body,
			lockedHeader : header[1]
		});

		var g = this.grid;

		g.getGridEl().dom.innerHTML = html;

		this.initElements();

		var bd = this.renderRows();
		if (typeof bd == "string") {
          bd = new Array("","");
        } 
		this.mainBody.dom.innerHTML = bd[0];
		this.lockedBody.dom.innerHTML = bd[1];
		this.processRows(0, true);

		Ext.fly(this.innerHd).on("click", this.handleHdDown, this);
		Ext.fly(this.lockedInnerHd).on("click", this.handleHdDown, this);
		this.mainHd.on("mouseover", this.handleHdOver, this);
		this.mainHd.on("mouseout", this.handleHdOut, this);
		this.mainHd.on("mousemove", this.handleHdMove, this);
		this.lockedHd.on("mouseover", this.handleHdOver, this);
		this.lockedHd.on("mouseout", this.handleHdOut, this);
		this.lockedHd.on("mousemove", this.handleHdMove, this);
		this.mainWrap.dom.style.left = this.cm.getTotalLockedWidth();
		this.scroller.on('scroll', this.syncScroll, this);
		if (g.enableColumnResize !== false) {
			this.splitone = new Ext.grid.GridView.SplitDragZone(g,
					this.lockedHd.dom);
			this.splitone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
			this.splitone.setOuterHandleElId(Ext.id(this.mainHd.dom));
		}

		if (g.enableColumnMove) {
			this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g,
					this.innerHd);
			this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
		}

		if (g.enableHdMenu !== false) {
			if (g.enableColumnHide !== false) {
				this.colMenu = new Ext.menu.Menu({
					id : g.id + "-hcols-menu"
				});
				this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
				this.colMenu.on("itemclick", this.handleHdMenuClick, this);
			}
			this.hmenu = new Ext.menu.Menu({
				id : g.id + "-hctx"
			});
			this.hmenu.add({
				id : "asc",
				text : this.sortAscText,
				cls : "xg-hmenu-sort-asc"
			}, {
				id : "desc",
				text : this.sortDescText,
				cls : "xg-hmenu-sort-desc"
			});
			if (this.grid.enableColLock !== false) {
				this.hmenu.add('-', {
					id : "lock",
					text : this.lockText,
					cls : "xg-hmenu-lock"
				}, {
					id : "unlock",
					text : this.unlockText,
					cls : "xg-hmenu-unlock"
				});
			}

			if (g.enableColumnHide !== false) {
				this.hmenu.add('-', {
					id : "columns",
					text : this.columnsText,
					menu : this.colMenu,
					iconCls : 'x-cols-icon'
				});
			}
			this.hmenu.on("itemclick", this.handleHdMenuClick, this);

		}

		if (g.enableDragDrop || g.enableDrag) {
			var dd = new Ext.grid.GridDragZone(g, {
				ddGroup : g.ddGroup || 'GridDD'
			});
		}

		this.updateHeaderSortState();

	},

	layout : function() {
		if (!this.mainBody) {
			return;
		}
		var g = this.grid;

		var c = g.getGridEl(), cm = this.cm, expandCol = g.autoExpandColumn, gv = this;

		var lw = cm.getTotalLockedWidth();
		var csize = c.getSize(true);
		var vw = csize.width;

		if (vw < 20 || csize.height < 20) {
			return;
		}

		if (g.autoHeight) {
			this.scroller.dom.style.overflow = 'visible';
			this.lockedScroller.dom.style.overflow = 'visible';
		} else {
			this.el.setSize(csize.width, csize.height);

			var hdHeight = this.mainHd.getHeight();
			var vh = csize.height - (hdHeight);

			this.scroller.setSize(vw - lw, vh);
			var scrollbar = (this.scroller.dom.scrollWidth > this.scroller.dom.clientWidth)
					? 17
					: 0;
			this.lockedScroller.setSize(cm.getTotalLockedWidth(), vh
					- scrollbar);
			if (this.innerHd) {
				this.innerHd.style.width = (vw) + 'px';
			}
		}
		if (this.forceFit) {
			if (this.lastViewWidth != vw) {
				this.fitColumns(false, false);
				this.lastViewWidth = vw;
			}
		} else {
			this.autoExpand();
		}
		this.mainWrap.dom.style.left = lw + 'px';

		this.onLayout(vw, vh);
	},

	renderHeaders : function() {
		var cm = this.cm, ts = this.templates;
		var ct = ts.hcell;
		var tw = this.cm.getTotalWidth();
		var lw = this.cm.getTotalLockedWidth();

		var cb = [], lb = [], sb = [], lsb = [], p = {};

		for (var i = 0, len = cm.getColumnCount(); i < len; i++) {
			p.id = cm.getColumnId(i);
			p.value = cm.getColumnHeader(i) || "";
			p.style = this.getColumnStyle(i, true);
			if (cm.config[i].align == 'right') {
				p.istyle = 'padding-right:16px';
			}
			if (cm.isLocked(i)) {
				lb[lb.length] = ct.apply(p);
			} else {
				cb[cb.length] = ct.apply(p);
			}
		}
		return [ts.header.apply({
			cells : cb.join(""),
			tstyle : 'width:' + (tw - lw) + ';'
		}), ts.header.apply({
			cells : lb.join(""),
			tstyle : 'width:' + (lw) + ';'
		})];
	},

	updateHeaders : function() {
		var hd = this.renderHeaders();
		this.innerHd.firstChild.innerHTML = hd[0];
		this.lockedInnerHd.firstChild.innerHTML = hd[1];
	},

	insertRows : function(dm, firstRow, lastRow, isUpdate) {
		if (firstRow === 0 && lastRow == dm.getCount() - 1) {
			this.refresh();
		} else {
			if (!isUpdate) {
				this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
			}
			var html = this.renderRows(firstRow, lastRow);
			var before = this.getRow(firstRow);
			var beforeLocked = this.getLockedRow(firstRow);
			if (before) {
				Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
				Ext.DomHelper.insertHtml('beforeBegin', beforeLocked, html[1]);
			} else {
				Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom,
						html[0]);
				Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom,
						html[1]);
			}
			if (!isUpdate) {
				this.fireEvent("rowsinserted", this, firstRow, lastRow);
				this.processRows(firstRow);
			}
		}
	},

	getColumnData : function() {
		var cs = [], cm = this.cm, colCount = cm.getColumnCount();
		for (var i = 0; i < colCount; i++) {
			var name = cm.getDataIndex(i);
			cs[i] = {
				name : (typeof name == 'undefined'
						? this.ds.fields.get(i).name
						: name),
				renderer : cm.getRenderer(i),
				id : cm.getColumnId(i),
				style : this.getColumnStyle(i),
				locked : cm.isLocked(i)
			};
		}
		return cs;
	},

	renderBody : function() {
		var markup = this.renderRows();
		return [this.templates.body.apply({
			rows : markup[0]
		}), this.templates.body.apply({
			rows : markup[1]
		})];
	},

	refresh : function(headersToo) {
		this.fireEvent("beforerefresh", this);
		this.grid.stopEditing();

		var result = this.renderBody();
		this.mainBody.update(result[0]);
		this.lockedBody.update(result[1]);

		if (headersToo === true) {
			this.updateHeaders();
			this.updateHeaderSortState();
		}
		this.processRows(0, true);
		this.layout();
		this.applyEmptyText();
		this.fireEvent("refresh", this);
	},

	handleLockChange : function() {
		this.refresh(true);
	},

	onDenyColumnHide : function() {

	},

	onColumnLock : function() {
		this.handleLockChange.apply(this, arguments);
	},

	addRowClass : function(row, cls) {
		var r = this.getRow(row);
		if (r) {
			this.fly(r).addClass(cls);
			r = this.getLockedRow(row);
			this.fly(r).addClass(cls);
		}
	},

	removeRowClass : function(row, cls) {
		var r = this.getRow(row);
		if (r) {
			this.fly(r).removeClass(cls);
			r = this.getLockedRow(row);
			this.fly(r).removeClass(cls);
		}
	},

	handleHdMenuClick : function(item) {
		var index = this.hdCtxIndex;
		var cm = this.cm, ds = this.ds;
		switch (item.id) {
			case "asc" :
				ds.sort(cm.getDataIndex(index), "ASC");
				break;
			case "desc" :
				ds.sort(cm.getDataIndex(index), "DESC");
				break;
			case "lock" :
				var lc = cm.getLockedCount();
				if (cm.getColumnCount(true) <= lc + 1) {
					//this.onDenyColumnLock();
					return;
				}
				if (lc != index) {
					cm.setLocked(index, true, true);
					cm.moveColumn(index, lc);
					this.grid.fireEvent("columnmove", index, lc);
				} else {
					cm.setLocked(index, true);
				}
				break;
			case "unlock" :
				var lc = cm.getLockedCount();
				if ((lc - 1) != index) {
					cm.setLocked(index, false, true);
					cm.moveColumn(index, lc - 1);
					this.grid.fireEvent("columnmove", index, lc - 1);
				} else {
					cm.setLocked(index, false);
				}
				break;
			default :
				index = cm.getIndexById(item.id.substr(4));
				if (index != -1) {
					if (item.checked
							&& cm.getColumnsBy(this.isHideableColumn, this).length <= 1) {
						this.onDenyColumnHide();
						return false;
					}
					cm.setHidden(index, item.checked);
				}
		}
		return true;
	},

	handleHdDown : function(e, t) {
		if (Ext.fly(t).hasClass('x-grid3-hd-btn')) {
			e.stopEvent();
			var hd = this.findHeaderCell(t);
			Ext.fly(hd).addClass('x-grid3-hd-menu-open');
			var index = this.getCellIndex(hd);
			this.hdCtxIndex = index;
			var ms = this.hmenu.items, cm = this.cm;
			ms.get("asc").setDisabled(!cm.isSortable(index));
			ms.get("desc").setDisabled(!cm.isSortable(index));
			if (this.grid.enableColLock !== false) {
				ms.get("lock").setDisabled(cm.isLocked(index));
				ms.get("unlock").setDisabled(!cm.isLocked(index));
			}
			this.hmenu.on("hide", function() {
				Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
			}, this, {
				single : true
			});
			this.hmenu.show(t, "tl-bl?");
		}
	}
});

Ext.grid.LockingColumnModel = function(config) {
	Ext.grid.LockingColumnModel.superclass.constructor.call(this, config);
};

Ext.extend(Ext.grid.LockingColumnModel, Ext.grid.ColumnModel, {
	getTotalLockedWidth : function() {
		var totalWidth = 0;
		for (var i = 0; i < this.config.length; i++) {
			if (this.isLocked(i) && !this.isHidden(i)) {
				totalWidth += this.getColumnWidth(i);
			}
		}
		return totalWidth;
	}
});
//EOP
;/**
 * Ext.ux.grid DragSelector Class
 *
 * @author           Claudio Walser aka Foggy cwa[at]uwd.ch
 * @copyright        2007-2008, UWD GmbH,  all rights reserved.


 * License: Ext.ux.grid DragSelector is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 * 
 **/
Ext.namespace('Ext.ux.grid');

Ext.ux.grid.DragSelector = function(cfg){
    cfg = cfg || {};
    var rs, rsOrig, objectsSelected = [];
    var grid, view, regions, proxy, tracker, selModel, scroller;
    var bodyRegion, mainRegion, dragRegion = new Ext.lib.Region(0,0,0,0);
    var dragSafe = cfg.dragSafe === true;
    var ctrlState, shiftState, isDragging = false;
    var scrollTopStart, scrollTop = 0;
    
    this.init = function(cmp){
        grid = cmp;
        view = grid.getView();
        selModel = grid.getSelectionModel();
        grid.on('render', onRender);
        grid.on('bodyscroll', syncScroll);
    };

    function syncScroll(e) {
        syncRowRegions();
        var top = scroller.getScroll().top;
        scrollTop = top - scrollTopStart;
        if (isDragging) {
            onDrag(e, true);
        }
    }

    function fillAllRegions(){
        mainRegion = scroller.getRegion();
        bodyRegion = scroller.getRegion();
        objectsSelected = [];
        var itemSelector = 'div.x-grid3-row';
        var mainElement = view.el.dom;
        Ext.each(Ext.query(itemSelector, mainElement), function(el) {
            objectsSelected[objectsSelected.length] = selModel.isSelected(objectsSelected.length);
        });
        fillRowRegions();
        syncScroll();
    }
    
    function fillRowRegions() {
        rs = [];
        rsOrig = [];
        var itemSelector = 'div.x-grid3-row';
        var mainElement = view.el.dom;
        Ext.each(Ext.query(itemSelector, mainElement), function(el) {
            rsOrig[rsOrig.length] = Ext.get(el).getRegion();
            rs[rs.length] = Ext.get(el).getRegion();
        });
    }
    
    function syncRowRegions() {
        fillRowRegions();
    }
    
    function cancelClick(e){
        ctrlState = e.ctrlKey;
        shiftState = e.shiftKey;
        grid.stopEditing();
        var target = e.getTarget();        
        if (!ctrlState && !shiftState && target.className === 'x-grid3-body') {
            selModel.clearSelections();
        }
        return true;
    }

    function onBeforeStart(e){
        // return false if is a right mouseclick
        if (e.button === 2) {
            return false;    
        }
        // return false if any grid editor is active
        if (grid.activeEditor && grid.activeEditor !== null) {
            return false;    
        }
        
        // return false if the header was clicked
        if (e.getPageY() <= view.el.getY() + 25) {
            return false;
        }
        // scrollbar fix from digitalbucket.net :)
        if (e.getPageX() > view.el.getX() + view.el.dom.clientWidth - 20) {
            return false;
        }
        
        // call cancelClick
        cancelClick(e);
        return !dragSafe || e.target == view.el.dom;
    }

    function onStart(e){
        scrollTopStart = scroller.getScroll().top;
        fillAllRegions();
        if(!proxy){
            proxy = view.el.createChild({cls:'x-view-selector'});
        } else {
            proxy.setDisplayed('block');
        }
        isDragging = true;
    }

    function onDrag(e, scaleSelector){
        var startXY = tracker.startXY;
        var xy = tracker.getXY();
        if (xy[0] < startXY[0] && !scaleSelector) {
            xy[0] += 2;    
        }
        if (scrollTop >= 0) {
            if ((startXY[1]- scrollTop) <= xy[1]) {
                var y = startXY[1] - scrollTop;
                var h = Math.abs(y - xy[1]);
            } else {
                var y = xy[1];    
                var h = Math.abs(startXY[1] - xy[1]) - scrollTop;
            }
            var x = Math.min(startXY[0], xy[0]);
            var w = Math.abs(startXY[0] - xy[0]);
            bodyRegion.top -= scrollTop;
        } else {
            if ((startXY[1] - scrollTop)  < xy[1]) {
                var y = startXY[1] - scrollTop;
                var h = Math.abs(y - xy[1]);
            } else {
                var y = xy[1]; // richtig in jedem fall
                var h = Math.abs((startXY[1] - scrollTop) - xy[1]) ;
            }
            
            var x = Math.min(startXY[0], xy[0]);
            var w = Math.abs(startXY[0] - xy[0]);
            
            bodyRegion.bottom -= scrollTop;
        }
        // set this values
        dragRegion.left = x;
        dragRegion.top = y ;
        dragRegion.right = x+w;
        dragRegion.bottom = y+h;
        
        dragRegion.constrainTo(bodyRegion);
        proxy.setRegion(dragRegion);

        for(var i = 0, len = rs.length; i < len; i++){
            var r = rs[i], sel = dragRegion.intersect(r);
            var selected = selModel.isSelected(i);
            var selectedBefore = objectsSelected[i];
            
            if (ctrlState) {
                if (selectedBefore) {
                    if(sel && selected){
                        selModel.deselectRow(i);
                    } else if(!sel && !selected){
                        selModel.selectRow(i, true);
                    }
                } else {
                    if(sel && !selected){
                        selModel.selectRow(i, true);
                    } else if(!sel && selected){
                        selModel.deselectRow(i);
                    }
                }
            } else {
                if(sel && !selected){
                    selModel.selectRow(i, true);
                } else if(!sel && selected){
                    selModel.deselectRow(i);
                }
            }
            
        }
        
        if (xy[1] + 10 >= mainRegion.bottom) {
            // slow up for ie
            if (Ext.isIE) {
                setTimeout(function() {
                    scroller.scrollTo('top', scroller.getScroll().top + 40);
                }, 100);
            } else {
                scroller.scrollTo('top', scroller.getScroll().top + 40);
            }
        }
        
        if (xy[1] - 10 <= mainRegion.top) {
            // slow up for ie
            if (Ext.isIE) {
                setTimeout(function() {
                    scroller.scrollTo('top', scroller.getScroll().top - 40);
                }, 100);
            } else {
                scroller.scrollTo('top', scroller.getScroll().top - 40);
            }
        }
    }

    function onEnd(e){
        isDragging = false;
        if(proxy){
            proxy.setDisplayed(false);
        }
    }
    
    function onRender(view){
        tracker = new Ext.dd.DragTracker({
            onBeforeStart: onBeforeStart,
            onStart: onStart,
            onDrag: onDrag,
            onEnd: onEnd
        });
        tracker.initEl(view.el);
        scroller = Ext.get(grid.getView().scroller);
    }
};
//EOP
;/**
 * Grid state manager. 
 * 
 * Thanks: http://extjs.com/forum/showthread.php?p=56770
 */
Ext.grid.GridStateManager = function(gridId) {
	this.gridId = gridId;
	this.state = {};
};

/**
 * Initialises <code>grid</code> with states in <code>provider</code>.
 * @param {Grid} grid
 * @param {Provider} provider
 */
Ext.grid.GridStateManager.prototype.init = function(grid, provider) {
	this.provider = provider;
	this.grid = grid;
};

Ext.grid.GridStateManager.prototype.storeState = function() {
	this.provider.set(this.getStateKey(), this.state);
};

Ext.grid.GridStateManager.prototype.getStateKey = function() {
	return this.gridId;
};
Ext.grid.GridStateManager.prototype.updateState = function() {
	var cm = this.grid.getColumnModel();
	var ds = this.grid.getStore();
	var state = this.state;

	// Copy the current sort column/direction.
	//var sortState = ds.getSortState(); // {field: "..dataIndex of sort
	// field..", direction: "ASC"|"DESC"}
	//if (typeof(sortState) == "object") {
	//	if (("field" in sortState) && ("direction" in sortState)) {
	//		state.sortInfo = {};
	//		state.sortInfo.field = sortState.field;
	//		state.sortInfo.direction = sortState.direction;
	//	}
	//}
	
	var c = [];
	for (var i = 0; i < cm.config.length; i++) {
		c.push({id:cm.config[i].id,
		        hidden:cm.config[i].hidden,
		        width:cm.config[i].width
		        //locked:cm.config[i].locked
		});
	}
	var obj = Ext.encode(c);
	state.config = Ext.decode(obj);
};

Ext.grid.GridStateManager.prototype.restore = function() {
	var cm = this.grid.getColumnModel();
	var ds = this.grid.getStore();

	// Set listeners on all events that relate to grid changes.
	// Hence, state should be updated every time the grid changes.
	// You might want to do this differently. E.g. save state when a user
	// presses a button; when the page unloads, etc.
	//
	function setState() {
		this.updateState();
		this.storeState();
	}
	// NB: widthchange event on ColumnModel does not fire when columns are
	// resized. Must use grid columnresize event.
	this.grid.un("columnresize", setState, this);
	cm.un("hiddenchange", setState, this);
	cm.un("columnlockchange", setState, this);
	cm.un("columnmoved", setState, this);
	// ds.un("datachanged", setState, this);

	// Get the state to be restored to the grid. //
	var state = this.provider.get(this.getStateKey(), {});

	var col, colConfig, config, i, sort;
	if (state) {
		if ("config" in state) {
			config = state.config;

			// Restore Column Model config.
			for (i = 0;i < config.length; i++) {
				colConfig = config[i];
				col = cm.getIndexById(colConfig.id);
				if (col != -1) {
					if (col != i) {
						cm.moveColumn(col, i);
					}
					if ("hidden" in colConfig) {
						cm.setHidden(i, colConfig.hidden);
					}else {
						cm.setHidden(i, false);
					}
					//if ("locked" in colConfig) {
					//	cm.setLocked(i, colConfig.locked);
					//} else {
					//	cm.setLocked(i, false);
					//}
					if ("width" in colConfig) {
						cm.setColumnWidth(i, colConfig.width);
					} else {
						cm.setColumnWidth(i, 100);
					}
				}
			}
		}

		//if ("sortInfo" in state) {
		//	var sortInfo = state.sortInfo;
		//	if (("field" in sortInfo) && ("direction" in sortInfo)) {
		//		ds.setDefaultSort(sortInfo.field, sortInfo.direction);
		//		// If local, might want to sort straight away as well...
		//	}
		//}
	}

	this.grid.on("columnresize", setState, this);
	cm.on("hiddenchange", setState, this);
	cm.on("columnlockchange", setState, this);
	cm.on("columnmoved", setState, this);
	// ds.on("datachanged", setState, this);
};

//EOP
;/**
 * Thanks: http://extjs.com/forum/showthread.php?t=17257
 *
 * @class Ext.ux.Andrie.pPageSize
 * @extends Ext.PagingToolbar
 * A combobox control that glues itself to a PagingToolbar's pageSize configuration property.
 * @constructor
 * Create a new PageSize plugin.
 * @param {Object} config Configuration options
 * @author Andrei Neculau - andrei.neculau@gmail.com / http://andreineculau.wordpress.com
 * @version 0.1.1
 */
divo.grid.PageSizer = function(config){
	Ext.apply(this, config)
}

Ext.extend(divo.grid.PageSizer, Ext.util.Observable, {
	/**
	 * @cfg {String} beforeText
	 * Text to display before the comboBox
	 */
	beforeText: '页长',

	/**
	 * @cfg {String} afterText
	 * Text to display after the comboBox
	 */
	afterText: '',

	/**
	 * @cfg {Mixed} addBefore
	 * Toolbar item(s) to add before the PageSizer
	 */
	addBefore: '-',

	/**
	 * @cfg {Mixed} addAfter
	 * Toolbar item(s) to be added after the PageSizer
	 */
	addAfter: null,

	/**
	 * @cfg {Array} variations
	 * Variations used for determining pageSize options
	 */
	variations: [5, 10, 20, 50, 100],

	init: function(pagingToolbar){
		this.pagingToolbar = pagingToolbar
		this.pagingToolbar.on('render', this.onRender, this)
	},

	//private
	addToStore:function(value){
		if (value>0){
			this.sizes.push([value])
		}
	},

	//private
	updateStore: function(){
		var middleValue = this.pagingToolbar.pageSize,
				start
		middleValue = (middleValue>0)?middleValue:1
		this.sizes = []
		var v = this.variations
		for (var i=0, len=v.length; i<len; i++){
			this.addToStore(middleValue - v[v.length-1-i])
		}
		this.addToStore(middleValue)
		for (var i=0, len=v.length; i<len; i++){
			this.addToStore(middleValue + v[i])
		}

		this.combo.store.loadData(this.sizes)
		this.combo.setValue(this.pagingToolbar.pageSize)
	},

	changePageSize:function(value){
		var pt = this.pagingToolbar
		this.combo.collapse()
		value = parseInt(value) || parseInt(this.combo.getValue())
		value = (value>0)?value:1
		if (value < pt.pageSize){
			pt.pageSize = value
			var ap = Math.round(pt.cursor/value)+1
			var cursor = (ap-1)*value
			var store = pt.store
			store.suspendEvents()
			for (var i=0, len=cursor-pt.cursor; i<len; i++){
				store.remove(store.getAt(0))
			}
			while (store.getCount() > value){
				store.remove(store.getAt(store.getCount()-1))
			}
			store.resumeEvents()
			store.fireEvent('datachanged', store)
			pt.cursor = cursor
			var d = pt.getPageData()
			pt.afterTextEl.el.innerHTML = String.format(pt.afterPageText, d.pages)
			pt.field.dom.value = ap
			pt.first.setDisabled(ap == 1)
			pt.prev.setDisabled(ap == 1)
			pt.next.setDisabled(ap == d.pages)
			pt.last.setDisabled(ap == d.pages)
			pt.updateInfo()
		}else{
			this.pagingToolbar.pageSize = value
			this.pagingToolbar.doLoad(Math.floor(this.pagingToolbar.cursor/this.pagingToolbar.pageSize) * this.pagingToolbar.pageSize)
		}
		divo.saveProfileAsync({
			userId : divo.getUserId(),
			msgCode : this.gridId+'-pagesize',
			msgValue : value
		})
		this.updateStore()
	},

	//private
	onRender: function(){
		var component = Ext.form.ComboBox
		this.combo = new component({
			store:new Ext.data.SimpleStore({
				fields:['pageSize'],
				data:[]
			}),
			clearTrigger:false,
			displayField:'pageSize',
			valueField:'pageSize',
			editable:false,
			mode:'local',
			triggerAction:'all',
			width:50
		})
		this.combo.on('select', this.changePageSize, this)
        this.pagingToolbar.pageSize = this.getPageSize()
		this.updateStore()

		if (this.addBefore){
			this.pagingToolbar.add(this.addBefore)
		}
		if (this.beforeText){
			this.pagingToolbar.add(this.beforeText)
		}
		this.pagingToolbar.add(this.combo)
		if (this.afterText){
			this.pagingToolbar.add(this.afterText)
		}
		if (this.addAfter){
			this.pagingToolbar.add(this.addAfter)
		}
	},
	/**
	 * 返回页长
	 */
	getPageSize : function() {
		var result
		divo.restoreProfile(function(retValue) {
		   result = retValue
		},divo.getUserId(), this.gridId+'-pagesize')
        return result?parseInt(result.msgValue):20
	}

})

//EOP
;/**
 * 网格类: 可以保存视图状态，要求从控制器中获得json格式的数据
 *
 * 注意：创建和修改视图的操作者，应该保证不要保存自己版本的视图，否则会看不到效果。
 *
 * -- 必备配置项
 * @container {String} 网格对应的div标识
 * @config {String} url 取数用url
 * @config {Object} meta 网格元数据
 * 
 * -- 数据来源
 * @config {bool} dataFromArray 真表示网格数据来自数组(默认为假)
 * @config {bool} lazyLoad 真表示延迟加载（默认为假，表示按正常方式加载数据)
 *
 * -- 可选配置项
 * @config {int} pageSize 分页长度（默认20）
 * @config {bool} displayLoadMask 是否显示数据加载提示（默认为true,表示显示提示）
 * @config {bool} remoteSort 是否需要后台排序(默认为true,表示后台排序)
 * @config {String} region 网格所在布局区域位置(默认为center)
 * @config {bool} editorGrid 是否允许网格直接修改(默认为false,表示不允许修改)
 * @config {Object/Array} plugins 插件对象
 * @config {String} jsonId json数据中的id字段名（默认为'id')
 *
 * -- 外观配置
 * @config {Array} toolbar 放置在工具条上的按钮
 * @config {bool} footer 是否显示页脚（默认为true,表示显示页脚）
 * @config {bool} footerSimple 是否显示简单页脚（只显示页脚左边页跳转相关内容，默认为false,真表示显示简单页脚）
 * @config {bool} filter 是否显示搜索框（默认为true,表示显示搜索框）. 如果不显示搜索框, 也没有添加工具条按钮,则不显示工具条.
 * @config {bool} pageSizer 是否显示页长选择框（默认为false,表示不显示）
 * @config {bool} autoRefresher 是否显示自动刷新间隔时间选择框（默认为false,表示不显示）
 * @config {bool} showTooltip 是否通过tooltip显示单元格内容(默认为false,表示不显示）
 * @config {bool} hideHeaders 是否显示网格列标题（默认为false,表示显示）
 * @config {String} autoExpandColumn 自动填充宽度的列的id值（默认为未定义）
 * @config {String} cls 定制样式表（默认无）(cls : 'vline-on' 表示显示网格竖线）
 * 
 * -- 行选择模式
 * @config {Object} selectionModel 自定义选择模式对象（默认为 RowSelectionModel）
 * @config {bool} singleSelect 是否只允许单选(默认为true,表示单选)
 * @config {bool} checkboxSelectionModel(默认为false,表示不用此模式)
 *
 * -- 拖动
 * @config {bool} enableDragDrop 是否允许拖动(默认为false,表示不允许)
 * @config {String} ddGroup 拖动时用的标识
 *
 * -- 导出到Excel
 * @config {bool} export2Excel 是否允许导出(默认为false,表示不允许导出)
 * @config {Function} export2ExcelCallback 函数，用于补充action传递参数
 *                     该函数返回值格式举例： &findType=0&projectId=1
 *
 * -- 视图
 * @config {String} defaultViewCode 默认视图（其他视图不再显示）
 * @config {String} gridIdOfView 视图保存用网格标识符（默认等于gridId+'-grid')
 * @config {bool} allowGridState 是否允许保留和恢复网格状态（默认为允许）
 *
 * -- 分组
 * @config {String} groupField （分组视图用）分组字段名(如果未定义，则表示不需要分组）
 * @config {String} groupTextTpl （分组视图用）分组行显示定制(默认值为'{text}')
 * @config {Object} groupSortInfo （分组视图用）默认排序定义(默认按分组字段名排序）
 * @config {bool} enableGroupingMenu （分组视图用）是否在列菜单中不允许出现分组菜单项(默认为不允许）
 * @config {bool} hideGroupedColumn （分组视图用）是否自动隐藏分组列（默认为隐藏）
 * @config {bool} showGroupName （分组视图用）是否在分组行显示分组字段名（默认为不显示）
 * @config {bool} enableNoGroups （分组视图用）是否允许用户取消分组（默认为不允许）
 *
 */
divo.grid.SmartGrid = function(container, config) {
	this.container = container
	this.cGridId = this.container
	this.arrayData = [] //数据原来是数组时，网格数据填入此变量中

	Ext.apply(this, config)
	this.addEvents( {
		/**
		 * @event viewselect 选择网格视图事件
		 * @param {SmartGrid}
		 *            this
		 * @param {GridViewData[Java]}
		 *            view
		 */
		'viewselect' : true
	})
	this.init()
}

Ext.extend(divo.grid.SmartGrid, Ext.util.Observable, {
	// private
	init : function() {
		this.cUserId = divo.getUserId();
		this.isViewEditor = this.cUserId==1 || divo.hasPermission("GridViewEditor")

		this.allowGridState = this.allowGridState==undefined ? true:this.allowGridState
		this.showTooltip = this.showTooltip==undefined ? false:this.showTooltip
		this.dataFromArray = this.dataFromArray==undefined ? false:this.dataFromArray
		this.pageSize = this.pageSize || 20;
		this.footer = this.footer == undefined ? true : this.footer;
		this.footerSimple = this.footerSimple == undefined ? false : this.footerSimple;
		this.filter = this.filter == undefined ? true : this.filter;
		this.hideHeaders = this.hideHeaders == undefined ? false : this.hideHeaders
		this.lazyLoad = this.lazyLoad==undefined?false:this.lazyLoad
		if (!this.url && !this.dataFromArray)
		    this.lazyLoad = true
		
		this.checkboxSelectionModel = this.checkboxSelectionModel == undefined ? false : this.checkboxSelectionModel;
		
		this.displayLoadMask = this.displayLoadMask == undefined
				? true
				: this.displayLoadMask;
		this.remoteSort = this.remoteSort == undefined
				? true
				: this.remoteSort;
		this.singleSelect = this.singleSelect == undefined
				? true
				: this.singleSelect;
		this.enableDragDrop = this.enableDragDrop == undefined
				? false
				: this.enableDragDrop;
		this.ddGroup = this.ddGroup || 'gridDD';
		this.region = this.region || 'center';
		this.tbarItems = [];
		if (this.toolbar) {
			this.tbarItems = this.toolbar;
		} else {
			if (!this.filter)
			    this.tbarItems = null;
		}
		if (!this.footer)
			this.pageSize = 10000; // 避免分页

		this.export2Excel = this.export2Excel == undefined ? false : this.export2Excel;
		this.editorGrid = this.editorGrid == undefined ? false : this.editorGrid;
		this.group = (this.groupField != undefined);
		this.groupTextTpl = this.groupTextTpl == undefined ? '{text}' : this.groupTextTpl;
		this.groupSortInfo = this.groupSortInfo == undefined ? {field: this.groupField, direction: "ASC"} : this.groupSortInfo;
		this.enableGroupingMenu = this.enableGroupingMenu == undefined ? false: this.enableGroupingMenu;
		this.hideGroupedColumn = this.hideGroupedColumn == undefined ? true: this.hideGroupedColumn;
		this.showGroupName = this.showGroupName == undefined ? false: this.showGroupName;
		this.enableNoGroups = this.enableNoGroups == undefined ? false: this.enableNoGroups;
		this.gridIdOfView = this.gridIdOfView || this.cGridId

		this.createHtml();
		this.createGrid();
		this.createGridViewForm();
		this.createGridState();
	},
	/**
	 * 网格初次加载
	 */
	load : function() {
		if (this.lazyLoad) return
		
		this.loaded = true
		if (this.dataFromArray)
			this.ds.loadData(this.arrayData)
		else	
			this.ds.load( {
				params : {
					start : 0,
					limit : this.pageSize
				}
			});
	},
	/**
	 * 刷新现有网格数据
	 */
	reload : function() {
		if (this.lazyLoad) return
		
		if (this.dataFromArray)
			this.ds.loadData(this.arrayData)
        else		
			if (!this.loaded)
			    this.load()
			else
			    this.ds.reload()
	},

	// private
	createGrid : function() {	
		if (this.dataFromArray) {
			this.ds = new Ext.data.Store( {
				reader : new Ext.data.ArrayReader( {}, this.meta.recordDef),
				data : this.arrayData
			})
		} else {
			if (!this.group) {
				this.ds = new Ext.data.Store( {
					proxy : new Ext.data.HttpProxy( {
						method : 'GET',
						url : this.url
					}),
					reader : new Ext.data.JsonReader( {
						root : 'rows',
						totalProperty : 'totalCount',
						id : this.jsonId || 'id'
					}, this.meta.recordDef),
					remoteSort : this.remoteSort
				});
			} else {
				var config = {
					proxy : new Ext.data.HttpProxy( {
						method : 'GET',
						url : this.url
					}),
					reader : new Ext.data.JsonReader( {
						root : 'rows',
						totalProperty : 'totalCount',
						id : this.jsonId || 'id'
					}, this.meta.recordDef),
					remoteSort : this.remoteSort	
				};
				if (this.groupField.length > 0) {
					config.groupField = this.groupField
				}
				config.sortInfo = this.groupSortInfo
				config.remoteGroup = true
	
				this.ds = new Ext.data.GroupingStore(config)
			}
		}
		
		if (this.footer) {
			this.createGridFooter();
		}
		this.showCurrentView(false);
		
		if (this.filter) {
			this.createGridHeader();
		}

		var sm;
		if (this.checkboxSelectionModel) {
			sm = new Ext.grid.CheckboxSelectionModel();
		} else {
			if (this.selectionModel) {
				sm = this.selectionModel;
			} else {
				sm = new Ext.grid.RowSelectionModel( {
					singleSelect : this.singleSelect
				});
			}
		}
		
		var ptPlugints = [];
		if (this.pageSizer) {
			var ps = new divo.grid.PageSizer({gridId:this.cGridId});
		    ptPlugints.push(ps);
		    this.pageSize = ps.getPageSize();
		}
		if (this.autoRefresher) {
			var ar = new divo.grid.AutoRefresher({gridId:this.cGridId});
		    ptPlugints.push(ar);
		}
		if (ptPlugints.length==0)
		    ptPlugints = undefined;

		var config = {
			el : this.container,
			region : this.region,
			stripeRows : true,
			store : this.ds,
			sm : sm,
			cm : this.meta.columnModel,
			hideHeaders : this.hideHeaders,
			autoExpandColumn : this.autoExpandColumn,
			loadMask : this.displayLoadMask?divo.loadMask:false,
			tbar: this.tbarItems,
			bbar : this.footer?new Ext.PagingToolbar( {
				plugins: ptPlugints,
				pageSize : this.pageSize,
				store : this.ds,
				displayInfo : true,
				items : this.bbarItems
			}):null,
	        enableDragDrop:this.enableDragDrop,
		  	ddGroup:this.ddGroup
		}
		if (this.group) {
	        config.view = new Ext.grid.GroupingView({
	            forceFit: true,
	            groupTextTpl: this.groupTextTpl,
            	showGroupName: this.showGroupName,
            	enableNoGroups:this.enableNoGroups,
            	enableGroupingMenu:this.enableGroupingMenu,
            	hideGroupedColumn:this.hideGroupedColumn
	        })
		}
		if (this.plugins) {
			config.plugins = this.plugins
		}
		config.cls = this.cls

        if (this.editorGrid) {
		   this.grid = new Ext.grid.EditorGridPanel(config)
        } else { 
           if (this.showTooltip) {	
	           //Thanks：http://extjs.com/forum/showthread.php?t=19868&page=2
	           Ext.apply(config,{
			        onRender: function() {
			        	Ext.grid.GridPanel.prototype.onRender.apply(this, arguments)
			        	this.addEvents("beforetooltipshow")
				        this.tooltip = new Ext.ToolTip({
				        	renderTo: Ext.getBody(),
				        	target: this.view.mainBody,
				        	listeners: {
				        		beforeshow: function(qt) {
				        			var v = this.getView();
						            var row = v.findRowIndex(qt.baseTarget)
						            var cell = v.findCellIndex(qt.baseTarget)
						            var cellEl = v.getCell(row,cell)
						            this.fireEvent("beforetooltipshow", this, row, cell, (cellEl?cellEl.textContent:''))
				        		},
				        		scope: this
				        	}
				        })
			        },
					listeners: {
						render: function(g) {
							g.on("beforetooltipshow", function(grid, row, col, cellValue) {
								grid.tooltip.body.update(cellValue)
							});
						}
					}
	           })
           }
           if (this.viewConfig)
               config.viewConfig = this.viewConfig
           
   		   this.grid = new Ext.grid.GridPanel(config)
        }

		this.ds.on('beforeload', function() {
			this.ds.baseParams.viewCode = this.cView?this.cView.code:'';
			this.ds.baseParams.filterValue =
				this.filterCombo?this.filterCombo.getValue():'';
			this.ds.baseParams.filterField = this.getFilterField();
		 }, this, true);

		this.stateRestored = false;

		this.ds.on("load", function() {
			 this.restoreGridState();
		}, this, true);
		
		this.grid.on("rowcontextmenu",this.showContextMenu,this)
	},
	/**
	 * 为网格添加页脚附加工具条按钮，并显示当前视图列表
	 */
	createGridFooter : function() {
		this.bbarItems = [];

		if (!this.footerSimple) {
			
			this.bbarItems.push('-');
			this.bbarItems.push({text:'查看:'});
	
			this.viewListBtn = new Ext.Toolbar.MenuButton( {
				enableToggle : false,
				menu : {
					items : []
				}
			});
			this.bbarItems.push(this.viewListBtn);
		}
	},
	showContextMenu : function(grid, rowIndex, e) {
		this.createContextMenu()
		
        e.stopEvent();
        if (this.contextMenu)
        	this.contextMenu.showAt(e.getXY());
	},
	createContextMenu : function() {
		if (this.contextMenu) return
		
		var items = []
		if (this.allowGridState) {		
			if (this.isViewEditor) {
				items.push({
					icon : divo.iconUrl + "divo/view-add.png",
					cls : 'x-btn-icon',
					text : '新建视图',
					handler : this.onAddGridView.createDelegate(this)
	            })
	            items.push({
					icon : divo.iconUrl + "divo/view-edit.png",
					cls : 'x-btn-icon',
					text : '修改视图',
					handler : this.onUpdateGridView.createDelegate(this)
	            })
	            items.push({
					icon : divo.iconUrl + "divo/view-delete.png",
					cls : 'x-btn-icon',
					text : '删除视图',
					handler : this.onDeleteGridView.createDelegate(this)
	            })
	            items.push('-')
			}
	        items.push({
				icon : divo.iconUrl + "divo/view-save.gif",
				cls : 'x-btn-icon',
				text : '保存我的视图',
				handler : this.onSaveMyGridView.createDelegate(this)
	        })
	        items.push({
				icon : divo.iconUrl + "divo/restore.gif",
				cls : 'x-btn-icon',
				text : '恢复视图到默认状态',
				handler : this.onRestoreMyGridView.createDelegate(this)
	        })
		}
		if (this.export2Excel) {
			if (items.length>0)
				items.push('-')
            items.push({
				icon : divo.iconUrl + "divo/excel.gif",
				cls : 'x-btn-icon',
				text : '导出到Excel',
				handler : this.onExport2Excel.createDelegate(this)
            })
		}
		if (items.length>0) {
			this.contextMenu = new Ext.menu.Menu({
	             items: items
	        })
		}
	},
	// private
	restoreGridState : function() {
		if (this.stateRestored)
			return
		this.stateRestored = true
		if (this.allowGridState)
		   this.gridState.restore()
	},
	// private
	onAddGridView : function(btn) {
		
		this.fldName.dom.value = '全部记录';
		this.saveViewNameBtn.dom.value = '新建';
		this.saveViewNameBtn.removeAllListeners();
		this.saveViewNameBtn.on('click', this.onAddSaveGridView, this);
		if (this.viewListBtn)
			this.gridViewForm.alignTo(this.viewListBtn.getEl(), 'tl', [3, -50]);
		else	
			this.gridViewForm.alignTo(this.grid.getEl(), 'c', [0, 0]);
		this.gridViewForm.show();
	},
	// private
	onUpdateGridView : function(btn) {
		if (!this.cView) return
		
		this.fldName.dom.value = this.cView.name;
		this.saveViewNameBtn.dom.value = '修改';
		this.saveViewNameBtn.removeAllListeners();
		this.saveViewNameBtn.on('click', this.onUpdateSaveGridView, this);
		if (this.viewListBtn)
			this.gridViewForm.alignTo(this.viewListBtn.getEl(), 'tl', [3, -50]);
		else	
			this.gridViewForm.alignTo(this.grid.getEl(), 'c', [0, 0]);
		this.gridViewForm.show();
	},
	// private
	onDeleteGridView : function() {
		if (!this.cView) return
		
		var ok = true
		Ext.Ajax.request({
			scope : this,
			async : false,
			url : '/gridviews/'+this.gridIdOfView+'/'+this.cView.id,
			method : 'DELETE',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				ok = resp.success
			},
			failure : function(response, options) {
				ok = false
			}
		});
		if (!ok) {
			divo.StatusBar.say("视图删除失败")
		    return
		}
		
		this.cView = null;
		this.showCurrentView(true);
		this.saveCurrentView();
		this.stateRestored = false;
		this.load();
	},
	// private
	createGridViewForm : function() {
		this.fldName = Ext.get('grid-view-name');
		this.gridViewForm = Ext.get('grid-view');
		this.saveViewNameBtn = Ext.get('grid-view-btn');
		var closeBtn = Ext.get('grid-view-close');
		closeBtn.on('click', this.gridViewForm.hide, this.gridViewForm, true);
	},
	// private
	onAddSaveGridView : function() {
		var name = this.fldName.dom.value;
		if (name.length == 0) {
			divo.StatusBar.say("视图名称不能为空。");
			return;
		}
		var ok = true
		var newId
		Ext.Ajax.request({
			scope : this,
			url : '/gridviews/'+this.gridIdOfView,
			async : false,
			method : 'POST',
			jsonData : {userId:this.cUserId,name:name,code:name},
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				ok = resp.success
				newId = resp.id
			},
			failure : function(response, options) {
				ok = false
			}
		});
		if (!ok) {
			divo.StatusBar.say("视图新建失败(注意先调整列宽等，或名称重复, 或网格无数据)");
			return;
		}
		divo.StatusBar.say("视图新建成功")
		
		this.gridViewForm.hide();

		this.cView = {
			id : newId,
			name : name,
			gridId : this.gridIdOfView
		};
		this.saveCurrentView();
		this.showCurrentView(true);
	},
	// private
	onUpdateSaveGridView : function() {
		if (!this.cView) return
		
		var name = this.fldName.dom.value;
		if (name.length == 0) {
			divo.StatusBar.say("视图名称不能为空。");
			return;
		}
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : '/gridviews/'+this.gridIdOfView+'/'+this.cView.id,
			async : false,
			method : 'PUT',
			jsonData : {userId:this.cUserId,name:name,code:name},
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				ok = resp.success
			},
			failure : function(response, options) {
				ok = false
			}
		});
		if (!ok) {
			divo.StatusBar.say("视图修改失败，注意视图名称不能重复。");
			return;
		} 
		divo.StatusBar.say("视图修改成功")
		
		this.gridViewForm.hide();
		this.showCurrentView(true);
	},
	// private
	onSelectGridView : function(item) {
		for (var i = 0;i < this.viewList.length; i++) {
			if (this.viewList[i].name == item.text) {
				this.cView = this.viewList[i];
				break;
			}
		} // 按名称找到对应的视图对象
		this.saveCurrentView();
		this.showCurrentView(true);
		this.stateRestored = false;
		this.load();
	},
	// private
	onSaveMyGridView : function() {
		if (!this.cView) return
		
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : '/gridviews/'+this.gridIdOfView+'/'+this.cView.id+"/"+this.cUserId,
			async : false,
			method : 'POST',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				ok = resp.success
			},
			failure : function(response, options) {
				ok = false
			}
		});
		if (ok)
			divo.StatusBar.say("视图保存成功");
		else	
			divo.StatusBar.say("视图保存失败");
			
	},
	// private
	onRestoreMyGridView : function() {
		if (!this.cView) return
		
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : '/gridviews/'+this.gridIdOfView+'/'+this.cView.id+"/"+this.cUserId,
			async : false,
			method : 'DELETE',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				ok = resp.success
			},
			failure : function(response, options) {
				ok = false
			}
		});
		if (ok) {
			divo.StatusBar.say("视图已经恢复");
			this.showCurrentView(true);
			this.stateRestored = false;
			this.load();
		} else {	
			divo.StatusBar.say("视图恢复失败");
		}
	},
	// private
	onExport2Excel : function() {
		var fieldList,sort='',dir=''
		var cm = this.grid.getColumnModel()
		var ds = this.grid.getStore()

		var sortState = ds.getSortState()
	    if (typeof(sortState) == "object") {
		    if (("field" in sortState) && ("direction" in sortState)) {
		    	sort = sortState.field
		    	dir = sortState.direction
		    }
	    }
	    var fldNames = [],fldHeaders = []
	    for (var i = 0; i < cm.config.length; i++) {
	    	 if (!cm.config[i].hidden) {
				 fldNames.push(cm.config[i].dataIndex)
				 fldHeaders.push(cm.config[i].header)
	    	 } 
		}
 
		document.GridExportForm.action = this.url+'.xls';
		//if (this.export2ExcelCallback) {
		//   document.GridExportForm.action = this.url+'?export2excel=y'+this.export2ExcelCallback();
		//}
		document.GridExportForm.auth.value = Ext.Ajax.defaultHeaders.Authorization
		document.GridExportForm.fieldHeaders.value = fldHeaders.join('-')
		document.GridExportForm.fieldNames.value =  fldNames.join('-')
		document.GridExportForm.viewCode.value = this.cView?this.cView.code:''
		document.GridExportForm.sort.value = sort.length==0?'':sort
		document.GridExportForm.dir.value = dir;
		document.GridExportForm.filterValue.value = this.filterCombo?this.filterCombo.getValue():''
		document.GridExportForm.filterField.value = this.getFilterField()
		document.GridExportForm.submit()
	},
	// private
	saveCurrentView : function() {
		var lastViewId = this.cView?this.cView.id:0
		Ext.Ajax.request({
			scope : this,
			url : '/gridviews/'+this.gridIdOfView+"/"+this.cUserId+"/current",
			async : false,
			method : 'PUT',
			jsonData : {id:lastViewId,saveCurrent:this.defaultViewCode?false:true}
		})
	},
	// private
	showCurrentView : function(gridRendered) {
		this.getGridViewList()

		if (!this.viewList) {
			this.cView = null
			if (this.viewListBtn)
			    this.viewListBtn.setText("(无视图)")
			this.load()
			return
		}

		//如果有默认视图，则总是使用默认视图，其他视图不让看到
		if (this.defaultViewCode) {
			var defaultViewId = null
			for (var i = 0;i < this.viewList.length; i++) {
				var vv = this.viewList[i];
				if (vv.code == this.defaultViewCode) {
					defaultViewId = vv.id
					break;
				}
			}
			var lastViewId = defaultViewId			
		} else {
		//如果没有默认视图，则使用上次用过的视图	
		    var lastViewId = null
		    Ext.Ajax.request({
		    	scope : this,
				url : '/gridviews/'+this.gridIdOfView+"/"+this.cUserId+"/current",
		    	async : false,
		    	method : 'GET',
		    	success : function(response, options) {
		    		var resp = Ext.decode(response.responseText)
		    		if (resp.success) {
		    			lastViewId = resp.id+''
		    		}
		    	}
		    })
		}

		this.cView = this.viewList[0];
		if (lastViewId) {
			for (var i = 0;i < this.viewList.length; i++) {
				var vv = this.viewList[i].id + '';
				if (vv == lastViewId) {
					this.cView = this.viewList[i];
					break;
				}
			}
		}
		this.fireEvent('viewselect', this, this.cView);
		if (this.viewListBtn)
  		   this.viewListBtn.setText(this.cView.name);

		//视图恢复算法说明：
		//1.在这里把视图定义取出，存入MyProfile（saveCurrentView）
		//2.加载数据完成后，通过load事件执行restoreGridState方法。
		//3.由类Ext.grid.GridStateManager负责从MyProfile取出并恢复网格视图。
        this.saveCurrentView() 
  			
		if (gridRendered) 
			this.reload()
	},
	// private
	getGridViewList : function() {
		var result;
        var ok = true
        Ext.Ajax.request({
        	scope : this,
        	url : "/gridviews/"+this.gridIdOfView,
        	async : false,
        	method : 'GET',
        	success : function(response, options) {
        		var resp = Ext.decode(response.responseText)
       			result = resp.rows
        	},
        	failure : function(response, options) {
        		ok = false
        	}
        });
        if (!ok) {
        	divo.StatusBar.say("取视图列表失败.")
        	return
        }
        
		this.viewList = []
		if (this.defaultViewCode) {
			if (result)
				for (var i = 0;i < result.length; i++) {
					var vv = result[i]
					if (vv.code == this.defaultViewCode) {
						this.viewList.push(result[i])
						break
					}
				} //只让看到默认视图
		} else {
			if (result)
				for (var i = 0;i < result.length; i++) {
					var vv = result[i]
					if (vv.code.indexOf('hidden')<0) {
						this.viewList.push(result[i])
					}
				} //剔除code中带有hidden的视图
		}
		
		if (!this.viewList || this.viewList.length == 0) {
			this.viewList = null
			return null;
		}	

		if (this.viewListBtn) {
			this.viewListBtn.menu.removeAll();
			for (var i = 0;i < this.viewList.length; i++) {
				this.viewListBtn.menu.addMenuItem( {
					text : this.viewList[i].name,
					handler : this.onSelectGridView.createDelegate(this)
				});
			}
		}
	},
	// private
	createGridState : function() {
		this.gridState = new Ext.grid.GridStateManager(this.gridIdOfView)
		this.gridState.init(this.grid, new divo.StateProvider())
	},
	/**
	 * 呈现Grid
	 */
	render : function() {
		this.grid.render();

		this.ds.on("loadexception", function(o,options,response,e) {
			 divo.showError(response.responseText,'数据加载失败')
		}, this, true);
	},
	// private
	createHtml : function() {
		var el = Ext.get('grid-view');
		if (el) return;

		// 视图新建或修改
		var html = [
				'<div id="grid-view" style="visibility:hidden;">',
				'   <img id="grid-view-close" src="media/images/divo/tab-close.gif" />',
				'   <p id="grid-view-title" class="active-msg">视图名称:</p>',
				'   <input type="text" id="grid-view-name"/>',
				'   <input id="grid-view-btn" type="button" value="新建"/><br />',
				'</div>	'].join('');
		Ext.DomHelper.append(document.body, {
			html : html
		});

		// 导出到Excel
		var html2 = [
				'<div id="grid-export-form" class="x-hidden">',
				'    <form method="POST" name="GridExportForm">',
				'         <input type="hidden" name="auth"/>',
				'         <input type="hidden" name="fieldNames"/>',
				'         <input type="hidden" name="fieldHeaders"/>',
				'         <input type="hidden" name="viewCode"/>',
				'         <input type="hidden" name="sort"/>',
				'         <input type="hidden" name="dir"/>',
				'         <input type="hidden" name="filterValue"/>',
				'         <input type="hidden" name="filterField"/>',
				'    </form>',
				'</div>	'].join('');
		Ext.DomHelper.append(document.body, {
			html : html2
		});
	},
	// private
	createGridHeader : function() {
		this.tbarItems.push("->")

		var l = this.meta.fs.length;
		if (!this.meta.fs || l == 0) {
			this.tbarItems.push({text:"搜索:&nbsp;&nbsp;"});
		} else {
			var menus = [{
				text : '搜索:',
				handler : this.onFilterClick.createDelegate(this)
			}];
			for (var i = 0;i < l; i++) {
				menus.push( {
					text : this.meta.fs[i].text,
					handler : this.onFilterClick.createDelegate(this)
				});
			}
			this.filterBtn = new Ext.Toolbar.MenuButton( {
				text : '搜索:',
				enableToggle : false,
				menu : {
					items : menus
				}
			});
			this.tbarItems.push(this.filterBtn);
		}

		// Create the filter field
		this.filterCombo = new Ext.ux.form.HistoryClearableComboBox( {
			selectOnFocus : true,
			emptyText : ' ',
			resizable : true,
			hideClearButton : false,
			hideTrigger : false,
			typeAhead : true,
			width:150,
			triggerAction : 'all'
		});
		this.tbarItems.push(this.filterCombo);

		// press enter keyboard
		this.filterCombo.on('specialkey', this.onSearchKeyPress
				.createDelegate(this));
		this.filterCombo.on('clearvalue', this.onSearch.createDelegate(this));
		this.filterCombo.on('select', this.onSearch.createDelegate(this));

		this.tbarItems.push( {
			icon : divo.iconSearch,
			cls : 'x-btn-icon',
			tooltip : '开始搜索(或按回车键)',
			tooltipType : 'title',
			handler : this.onSearch.createDelegate(this)
		});
	},
	// private
	getFilterField : function() {
		if (this.filterBtn) {
			var fld = this.filterBtn.getText();
			var l = this.meta.fs.length;
			for (var i = 0;i < l; i++) {
				if (fld === this.meta.fs[i].text)
					return this.meta.fs[i].name;
			}
		}
		return '';
	},
	// private
	onFilterClick : function(item) {
		this.filterBtn.setText(item.text);

		if (this.filterCombo.getValue() != '')
			this.load();
	},
	// private
	onSearchKeyPress : function(o, e) {
		if (e.getKey() === e.ENTER) {
			this.load();
		}
	},
	// private
	onSearch : function() {
		this.load();
	}

});
// EOP

;// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * RowActions plugin for Ext grid
 *
 * Contains renderer for icons and fires events when an icon is clicked
 *
 * @author    Ing. Jozef Sakáloš
 * @date      22. March 2008
 * @version   $Id: Ext.ux.grid.RowActions.js 150 2008-04-08 21:50:58Z jozo $
 *
 * @license Ext.ux.grid.RowActions is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/*global Ext */

Ext.ns('Ext.ux.grid');

/**
 * @class Ext.ux.grid.RowActions
 * @extends Ext.util.Observable
 *
 * CSS rules from Ext.ux.RowActions.css are mandatory
 *
 * Important general information: Actions are identified by iconCls. Wherever an <i>action</i>
 * is referenced (event argument, callback argument), the iconCls of clicked icon is used.
 * In another words, action identifier === iconCls.
 *
 * Creates new RowActions plugin
 * @constructor
 * @param {Object} config The config object
 */
Ext.ux.grid.RowActions = function(config) {
	Ext.apply(this, config);

	// {{{
	this.addEvents(
		/**
		 * @event beforeaction
		 * Fires before action event. Return false to cancel the subsequent action event.
		 * @param {Ext.grid.GridPanel} grid
		 * @param {Ext.data.Record} record Record corresponding to row clicked
		 * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
		 * @param {Integer} rowIndex Index of clicked grid row
		 * @param {Integer} colIndex Index of clicked grid column that contains all action icons
		 */
		 'beforeaction'
		/**
		 * @event action
		 * Fires when icon is clicked
		 * @param {Ext.grid.GridPanel} grid
		 * @param {Ext.data.Record} record Record corresponding to row clicked
		 * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
		 * @param {Integer} rowIndex Index of clicked grid row
		 * @param {Integer} colIndex Index of clicked grid column that contains all action icons
		 */
		,'action'
		/**
		 * @event beforegroupaction
		 * Fires before group action event. Return false to cancel the subsequent groupaction event.
		 * @param {Ext.grid.GridPanel} grid
		 * @param {Array} records Array of records in this group
		 * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
		 * @param {String} groupId Identifies the group clicked
		 */
		,'beforegroupaction'
		/**
		 * @event groupaction
		 * Fires when icon in a group header is clicked
		 * @param {Ext.grid.GridPanel} grid
		 * @param {Array} records Array of records in this group
		 * @param {String} action Identifies the action icon clicked. Equals to icon css class name.
		 * @param {String} groupId Identifies the group clicked
		 */
		,'groupaction'
	);
	// }}}

	// call parent
	Ext.ux.grid.RowActions.superclass.constructor.call(this);
};

Ext.extend(Ext.ux.grid.RowActions, Ext.util.Observable, {

	// configuration options
	// {{{
	/**
	 * @cfg {Array} actions Mandatory. Array of action configuration objects. The following
	 * configuration options of action are recognized:
	 *
	 * - @cfg {Function} callback Optional. Function to call if the action icon is clicked.
	 *   This function is called with same signature as action event and in its original scope.
	 *   If you need to call it in different scope or with another signature use 
	 *   createCallback or createDelegate functions. Works for statically defined actions. Use
	 *   callbacks configuration options for store bound actions.
	 *
	 * - @cfg {Function} cb Shortcut for callback.
	 *
	 * - @cfg {String} iconIndex Optional, however either iconIndex or iconCls must be
	 *   configured. Field name of the field of the grid store record that contains
	 *   css class of the icon to show. If configured, shown icons can vary depending
	 *   of the value of this field.
	 *
	 * - @cfg {String} iconCls. css class of the icon to show. It is ignored if iconIndex is
	 *   configured. Use this if you want static icons that are not base on the values in the record.
	 *
	 * - @cfg {Boolean} hide Optional. True to hide this action while still have a space in 
	 *   the grid column allocated to it. IMO, it doesn't make too much sense, use hideIndex instead.
	 *
	 * - @cfg (string} hideIndex Optional. Field name of the field of the grid store record that
	 *   contains hide flag (falsie [null, '', 0, false, undefined] to show, anything else to hide).
	 *
	 * - @cfg {String} qtipIndex Optional. Field name of the field of the grid store record that 
	 *   contains tooltip text. If configured, the tooltip texts are taken from the store.
	 *
	 * - @cfg {String} tooltip Optional. Tooltip text to use as icon tooltip. It is ignored if 
	 *   qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
	 *
	 * - @cfg {String} qtip Synonym for tooltip
	 *
	 * - @cfg {String} textIndex Optional. Field name of the field of the grids store record
	 *   that contains text to display on the right side of the icon. If configured, the text
	 *   shown is taken from record.
	 *
	 * - @cfg {String} text Optional. Text to display on the right side of the icon. Use this
	 *   if you want static text that are not taken from record. Ignored if textIndex is set.
	 *
	 * - @cfg {String} style Optional. Style to apply to action icon container.
	 */

	/**
	 * @cfg {String} actionEvnet Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click')
	 */
	 actionEvent:'click'

	/**
	 * @cfg {Boolean} autoWidth true to calculate field width for iconic actions only.
	 */
	,autoWidth:true

	/**
	 * @cfg {Array} groupActions Array of action to use for group headers of grouping grids.
	 * These actions support static icons, texts and tooltips same way as actions. There is one
	 * more action config recognized:
	 * - @cfg {String} align Set it to 'left' to place action icon next to the group header text.
	 *   (defaults to undefined = icons are placed at the right side of the group header.
	 */

	/**
	 * @cfg {Object} callbacks iconCls keyed object that contains callback functions. For example:
	 * callbacks:{
	 *      'icon-open':function(...) {...}
	 *     ,'icon-save':function(...) {...}
	 * }
	 */

	/**
	 * @cfg {String} header Actions column header
	 */
	,header:''

	/**
	 * @cfg {Boolean} menuDisabled No sense to display header menu for this column
	 */
	,menuDisabled:true

	/**
	 * @cfg {Boolean} sortable Usually it has no sense to sort by this column
	 */
	,sortable:false

	/**
	 * @cfg {String} tplGroup Template for group actions
	 * @private
	 */
	,tplGroup:
		 '<tpl for="actions">'
		+'<div class="ux-grow-action-item<tpl if="\'right\'===align"> ux-action-right</tpl> '
		+'{cls}" style="{style}" qtip="{qtip}">{text}</div>'
		+'</tpl>'

	/**
	 * @cfg {String} tplRow Template for row actions
	 * @private
	 */
	,tplRow:
		 '<div class="ux-row-action">'
		+'<tpl for="actions">'
		+'<div class="ux-row-action-item {cls} <tpl if="text">'
		+'ux-row-action-text</tpl>" style="{hide}{style}" qtip="{qtip}">'
		+'<tpl if="text"><span qtip="{qtip}">{text}</span></tpl></div>'
		+'</tpl>'
		+'</div>'

	/**
	 * @private {Number} widthIntercept constant used for auto-width calculation
	 */
	,widthIntercept:4

	/**
	 * @private {Number} widthSlope constant used for auto-width calculation
	 */
	,widthSlope:21
	// }}}

	// methods
	// {{{
	/**
	 * Init function
	 * @param {Ext.grid.GridPanel} grid Grid this plugin is in
	 */
	,init:function(grid) {
		this.grid = grid;
		
		// {{{
		// setup template
		if(!this.tpl) {
			this.tpl = this.processActions(this.actions);

		} // eo template setup
		// }}}

		// calculate width
		if(this.autoWidth) {
			this.width =  this.widthSlope * this.actions.length + this.widthIntercept;
			this.fixed = true;
		}

		// body click handler
		var view = grid.getView();
		var cfg = {scope:this};
		cfg[this.actionEvent] = this.onClick;
		grid.on({
			render:{scope:this, fn:function() {
				view.mainBody.on(cfg);
			}}
		});

		// setup renderer
		if(!this.renderer) {
			this.renderer = function(value, cell, record, row, col, store) {
				cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell';
				return this.tpl.apply(this.getData(value, cell, record, row, col, store));
			}.createDelegate(this);
		}

		// actions in grouping grids support
		if(view.groupTextTpl && this.groupActions) {
			view.interceptMouse = view.interceptMouse.createInterceptor(function(e) {
				if(e.getTarget('.ux-grow-action-item')) {
					return false;
				}
			});
			view.groupTextTpl = 
				 '<div class="ux-grow-action-text">' + view.groupTextTpl +'</div>' 
				+this.processActions(this.groupActions, this.tplGroup).apply()
			;
		}
		
	} // eo function init
	// }}}
	// {{{
	/**
	 * Returns data to apply to template. Override this if needed.
	 * @param {Mixed} value 
	 * @param {Object} cell object to set some attributes of the grid cell
	 * @param {Ext.data.Record} record from which the data is extracted
	 * @param {Number} row row index
	 * @param {Number} col col index
	 * @param {Ext.data.Store} store object from which the record is extracted
	 * @returns {Object} data to apply to template
	 */
	,getData:function(value, cell, record, row, col, store) {
		return record.data || {};
	} // eo function getData
	// }}}
	// {{{
	/**
	 * Processes actions configs and returns template.
	 * @param {Array} actions
	 * @param {String} template Optional. Template to use for one action item.
	 * @return {String}
	 * @private
	 */
	,processActions:function(actions, template) {
		var acts = [];

		// actions loop
		Ext.each(actions, function(a, i) {
			// save callback
			if(a.iconCls && 'function' === typeof (a.callback || a.cb)) {
				this.callbacks = this.callbacks || {};
				this.callbacks[a.iconCls] = a.callback || a.cb;
			}

			// data for intermediate template
			var o = {
				 cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
				,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
				,text:a.textIndex ? '{' + a.textIndex + '}' : (a.text ? a.text : '')
				,hide:a.hideIndex ? '<tpl if="' + a.hideIndex + '">visibility:hidden;</tpl>' : (a.hide ? 'visibility:hidden;' : '')
				,align:a.align || 'right'
				,style:a.style ? a.style : ''
			};
			acts.push(o);

		}, this); // eo actions loop

		var xt = new Ext.XTemplate(template || this.tplRow);
		return new Ext.XTemplate(xt.apply({actions:acts}));

	} // eo function processActions
	// }}}
	// {{{
	/**
	 * Grid body actionEvent event handler
	 * @private
	 */
	,onClick:function(e, target) {

		var view = this.grid.getView();
		var action = false;

		// handle row action click
		var row = e.getTarget('.x-grid3-row');
		var col = view.findCellIndex(target.parentNode.parentNode);

		var t = e.getTarget('.ux-row-action-item');
		if(t) {
			action = t.className.replace(/ux-row-action-item /, '');
			if(action) {
				action = action.replace(/ ux-row-action-text/, '');
				action = action.trim();
			}
		}
		if(false !== row && false !== col && false !== action) {
			var record = this.grid.store.getAt(row.rowIndex);

			// call callback if any
			if(this.callbacks && 'function' === typeof this.callbacks[action]) {
				this.callbacks[action](this.grid, record, action, row.rowIndex, col);
			}

			// fire events
			if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, row.rowIndex, col)) {
				return;
			}
			else if(true !== this.eventsSuspended) {
				this.fireEvent('action', this.grid, record, action, row.rowIndex, col);
			}

		}

		// handle group action click
		t = e.getTarget('.ux-grow-action-item');
		if(t) {
			// get groupId
			var group = view.findGroup(target);
			var groupId = group ? group.id.replace(/ext-gen[0-9]+-gp-/, '') : null;

			// get matching records
			var records;
			if(groupId) {
				var re = new RegExp(groupId);
				records = this.grid.store.queryBy(function(r) {
					return r._groupId.match(re);
				});
				records = records ? records.items : [];
			}
			action = t.className.replace(/ux-grow-action-item (ux-action-right )*/, '');

			// call callback if any
			if('function' === typeof this.callbacks[action]) {
				this.callbacks[action](this.grid, records, action, groupId);
			}

			// fire events
			if(true !== this.eventsSuspended && false === this.fireEvent('beforegroupaction', this.grid, records, action, groupId)) {
				return false;
			}
			this.fireEvent('groupaction', this.grid, records, action, groupId);
		}
	} // eo function onClick
	// }}}

});

// registre xtype
Ext.reg('rowactions', Ext.ux.grid.RowActions);

// eof
;/**
 * Ext.ux.grid.ProgressColumn - Ext.ux.grid.ProgressColumn is a grid plugin that
 * shows a progress bar for a number between 0 and 100 to indicate some sort of
 * progress. The plugin supports all the normal cell/column operations including
 * sorting, editing, dragging, and hiding. It also supports special progression
 * coloring or standard Ext.ProgressBar coloring for the bar.
 *
 * @author Benjamin Runnels <kraven@kraven.org>
 * @copyright (c) 2008, by Benjamin Runnels
 * @date 06 June 2008
 * @version 1.1
 *
 * @license Ext.ux.grid.ProgressColumn is licensed under the terms of the Open
 *          Source LGPL 3.0 license. Commercial use is permitted to the extent
 *          that the code/component(s) do NOT become part of another Open Source
 *          or Commercially licensed development library or toolkit without
 *          explicit permission.
 *
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

Ext.namespace('Ext.ux.grid');

Ext.ux.grid.ProgressColumn = function(config) {
  Ext.apply(this, config);
  this.renderer = this.renderer.createDelegate(this);
  this.addEvents('action');
  Ext.ux.grid.ProgressColumn.superclass.constructor.call(this);
};

Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, {
  /**
   * @cfg {String} colored determines whether use special progression coloring
   *      or the standard Ext.ProgressBar coloring for the bar (defaults to
   *      false)
   */
  textPst : '%',
  /**
   * @cfg {String} colored determines whether use special progression coloring
   *      or the standard Ext.ProgressBar coloring for the bar (defaults to
   *      false)
   */
  colored : false,
  /**
   * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick,
   *      mouseover (defaults to 'dblclick')
   */
  actionEvent : 'dblclick',

  init : function(grid) {
    this.grid = grid;
    this.view = grid.getView();

    if (this.editor && grid.isEditor) {
      var cfg = {
        scope : this
      };
      cfg[this.actionEvent] = this.onClick;
      grid.afterRender = grid.afterRender.createSequence(function() {
        this.view.mainBody.on(cfg);
      }, this);
    }
  },

  onClick : function(e, target) {
    var rowIndex = e.getTarget('.x-grid3-row').rowIndex;
    var colIndex = this.view.findCellIndex(target.parentNode.parentNode);

    var t = e.getTarget('.x-progress-text');
    if (t) {
      this.grid.startEditing(rowIndex, colIndex);
    }
  },

  renderer : function(v, p, record) {
    var style = '';
    var textClass = (v < 55) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : '');

    //ugly hack to deal with IE6 issue
    var text = String.format('</div><div class="x-progress-text {0}" style="width:100%;" id="{1}">{2}</div></div>',
      textClass, Ext.id(), v + this.textPst
    );
    text = (v<96) ? text.substring(0, text.length - 6) : text.substr(6);

    if (this.colored == true) {
      if (v <= 100 && v > 66)
        style = '-green';
      if (v < 67 && v > 33)
        style = '-orange';
      if (v < 34)
        style = '-red';
    }

    p.css += ' x-grid3-progresscol';
    return String.format(
      '<div class="x-progress-wrap"><div class="x-progress-inner"><div class="x-progress-bar{0}" style="width:{1}%;">{2}</div>' +
      '</div>', style, v, text
    );
  }
});;/**
 * 通用SmartGrid显示面板
 * 
 * --------------------------------------------------------------
 * 消息：
 * 用户选择了网格中的某行
 * 
 * 消息名：     			
 * divo.rowSelect+this.id   
 * 
 * 消息内容：
 * {int} sender 本组件的id值
 * {int} id 网格当前行对应的记录id值（无网格记录时为null）
 * {String} name 网格当前行对应的记录name字段值（无网格记录时为null）
 * {Ext.data.Record} record 网格当前行
 * --------------------------------------------------------------
 */
divo.panel.SmartGridPanel = Ext.extend(Ext.Panel, {
	url : "",
	gridConfig : null,
    fieldDefinitions : null,
	initComponent : function() {
		this.meta = this.createGridMeta()
		this.gridId = this.myId('grid')
		this.createGrid()

		Ext.apply(this, {
			gridId : this.gridId,
			region : 'center',
			border : false,
			layout : 'border',
			items : [this.grid]
		})

		this.addEvents({
			'dblclick' : true
		}); 
		
		divo.panel.SmartGridPanel.superclass.initComponent.apply(this, arguments)

	},
	render : function() {
		divo.panel.SmartGridPanel.superclass.render.apply(this,arguments)
		this.doLayout()

		this.smartGrid.render()
		
		this.selModel.on('rowselect', this.onGridRowSelect, this)
		this.grid.on('dblclick', this.onSelect, this)	
		this.ds.on("load", this.onGridLoad, this)
		this.ds.on('beforeload', this.onBeforeLoad, this)
	},
	afterRender : function() {
		divo.panel.SmartGridPanel.superclass.afterRender.apply(this,arguments)
		this.smartGrid.load()
	},
	// 创建网格元数据(子类可以重写)
	createGridMeta : function() {
		return this.getGridMeta(this.fieldDefinitions)
	},
	// 创建网格组件
	createGrid : function() {
		if (!Ext.get(this.gridId)) {
			Ext.DomHelper.append(document.body, {
				html : '<div id="' + this.gridId + '"></div>'
			});
		}
		var config = {
			url : this.url,
			meta : this.meta,
			filter : false,
			footerSimple : false
		}
		this.gridConfig.gridIdOfView = this.gridConfig.gridIdOfView+"xxxx"+this.gridId
		Ext.apply(config, this.gridConfig || {})

		this.smartGrid = new divo.grid.SmartGrid(this.gridId, config)
		this.grid = this.smartGrid.grid
		this.ds = this.smartGrid.ds
		this.selModel = this.grid.getSelectionModel()
	},
	//private
	//通过插件方式加入cmd的放入cmArray
	getGridMeta : function(result,cmArray) {
		var meta = {};
		meta.findColumn = function(name) {
			for (var i = 0;i < this.cms.length; i++) {
				if (this.cms[i].dataIndex === name)
					return i;
			}
			return null;
		} // 按名称查找返回列所在序号
		meta.findMetaIndex = function(name) {
			for (var i = 0;i < this.metas.length; i++) {
				if (this.metas[i].name === name)
					return i;
			}
			return null;
		} // 按名称查找返回元数据数组索引号

		if (!result) {
			meta.recordDef = Ext.data.Record.create([{
				name : ""
			}]);
			meta.columnModel = new Ext.grid.ColumnModel([{
				header : ""
			}]);
			return meta;
		}

		var rs = [], cms = [], fs = [], metas = [];
		for (var i = 0;i < result.length; i++) {
			metas.push( {
				name : result[i].name,
				header : result[i].header
			});
			rs.push( {
				name : result[i].name,
				type : result[i].type?result[i].type:'auto',
				convert : result[i].convert?result[i].convert:undefined
			});
			if (result[i].header) {
				cms.push( {
					header : result[i].header,
					dataIndex : result[i].name,
					type : result[i].type,
					renderer : result[i].renderer?result[i].renderer:null,
					id : result[i].id?result[i].id:undefined,
					width : result[i].width?result[i].width:undefined,
					css : result[i].css?result[i].css:undefined,
					sortable : result[i].sortable==undefined?true:result[i].sortable,
					editor : result[i].editor==undefined?undefined:result[i].editor
				});
				if (result[i].searchable)
					fs.push( {
						text : result[i].searchName?result[i].searchName:result[i].header,
						name : result[i].name
					});
			}
		}
			
		if (cmArray) {
			for (var i = 0; i < cmArray.length; i++) {
				if (cmArray[i])
					cms.push(cmArray[i])
			}
		}	
		
		meta.recordDef = Ext.data.Record.create(rs);
		meta.columnModel = new Ext.grid.ColumnModel(cms);
		meta.columnModel.defaultSortable = true;
		meta.rs = rs;
		meta.cms = cms;
		meta.fs = fs;
		meta.metas = metas;
		return meta;
	},
	onGridRowSelect : function (grid, rowIndex, e) {
		var record = this.selModel.getSelections()[0]
		this.cRowIndex = rowIndex
		this.cRecordData = record.data
		if (this.cIdFocus)
		   this.cId = this.cIdFocus
		else
		   this.cId = this.cRecordData.id
		   
		this.cIdFocus = null
		this.cName = this.cRecordData.name

		this.notifyRowChange(record)
		this.setToolbarStatus()
	},
	notifyRowChange : function(record) {
		this.publish("divo.rowSelect"+this.id,{
			sender:this.id,
			id:this.cId,
			name:this.cName,
			record:record})
	},
	setToolbarStatus : Ext.emptyFn,
	onGridLoad : function(store, records, options) {
		if (this.ds.getTotalCount() == 0) {
			this.cId = null
			this.cName = null
			this.notifyRowChange()
		} else {
			if (this.cIdFocus)
			    this.cId
			    
			if (this.cId ) {
				var i = this.ds.indexOfId(this.cId)
				if (i>=0) {
					this.grid.getView().focusRow(i)
					this.selModel.selectRow(i)
				} else {
					this.selModel.selectFirstRow()
				}
			} else {
				this.selModel.selectFirstRow()
			}
		}
		this.setToolbarStatus()
	},
	loadList : function(arrayData) {
		if (arrayData)
		    this.smartGrid.arrayData = arrayData
		this.onLoadList()
	},
	onLoadList : function() {
		this.smartGrid.reload()
	},
	//public
	setUrl : function(url) {
		this.cId = null
		this.cName = null

		this.smartGrid.url = url
		this.smartGrid.lazyLoad = false
		this.ds.proxy.conn.url = url
	},
	/**
	 * LovField所需接口
	 */
	getSelectionModel : function() {
		return this.selModel
	},
	getStore : function() {
		return this.ds
	},
	onSelect : function(e) {
		this.fireEvent("dblclick")
		this.onDblClick()
	},
	//抽象方法（由子类实现）
	onDblClick : Ext.emptyFn,
	onBeforeLoad : Ext.emptyFn, //function(store, options)
	//public
	getCurrentRecord : function() {
		return this.grid.getSelectionModel().getSelections()[0]
	},
	//public
	checkSelection : function(msg) {
		if (!this.cId) {
			this.say(msg || "请先选择某条记录")
			return false
		}
		var record = this.getCurrentRecord()
		if (!record) {
			this.say(msg || "请先选择某条记录")
			return false
		}
		return true
	},
	deleteItem : function() {
		if (!this.cId) {
			this.say("请先选择要删除的记录")
			return
		}
		Ext.MessageBox.confirm("确认", "要删除吗?", this.deleteItemConfirm.createDelegate(this))
	},
	deleteItemConfirm : function(btn) {
		if (btn == "yes") {
			this.onDeleteItem(this.cId)
		}
	},
	//子类应重写此方法
	onDeleteItem : Ext.emptyFn,
	//删除成功后调用
	deleteItemCallback : function() {
		this.cId = null
		this.cName = null
		this.loadList()
	},
	rowNumberRender : function(value, p, record) {
		var ds = record.store
		var i = ds.lastOptions.params.start
		if (isNaN(i)) {
			i = 0;
		}
		return ds.indexOf(record)+i+1
	},
	addedOnRender : function(value, p, record) {
		return value.substr(0,10)
	},
	lineBreakRender : function(value, p, record) {
		return value.replace(/\n/g, "<br/>")
	},
	//局限：刷新后才能看到换行效果
    wordWrapRender : function(value,metadata,record,rowIndex,colIndex){
    	if (!this.grid.getColumnModel().config[colIndex].width)
    	    return value
		var begin = 0
		var finalText = ''
		var hzLen = value.getHzLength()
		var columnWidth = this.grid.getColumnModel().getColumnWidth(colIndex)
		columnWidth /= 7.8
		if(hzLen > columnWidth){
			return value.brkLine(columnWidth).replace(/\n/g, "<br/>")
		}else{
			return value.replace(/\n/g, "<br/>")
		}
	},
	alignRightRender: function(v, meta) {
  		meta.css = 'x-align-right'
  		return v
	},
	onRefreshList : function(subj, msg, data) {
		this.cIdFocus = this.cId
		this.loadList()
	}

})

// EOP
;/**
 * 具有自动生成新建和修改表单功能
 */
divo.panel.SmartGridDetailPanel = Ext.extend(divo.panel.SmartGridPanel, {
	form : null,
	dialog : null,
	dialogWidth : 500,
	dialogHeight : 300,
	dialogTitle : "",
	isLookup : false,  //同一个网格面板可以用于数据维护和字典查找,用于字典查找时设置为true 
	initComponent : function() {
		if (this.showToolbar) {
			this.tbar = new Ext.Toolbar()
		}
		divo.panel.SmartGridDetailPanel.superclass.initComponent.call(this)
	},
	fillForm : function(data) {
		this.showDialog()
		this.form.setItem(data)
		this.form.setFocus()
	},
	addItem : function() {
		this.fillForm({})
	},
	editItem : function() {
		if (!this.cId) {
			this.say("请先选择要修改的记录")
			return
		}
		this.onLoadItem(this.cId, this.fillForm.createDelegate(this))
	},
	cancelEdit : function() {
		this.hideDialog()
	},
	saveItem : function(btn) {
		var item = this.form.getUpdatedItem()
		if (!this.validateItem(item)) {
			return
		}
		this.saveBtn = btn
		btn.disable()
		this.onSaveItem(item)
	},
	//新建或修改保存后回调
	saveItemCallback : function(adding,resp) {
		this.saveBtn.enable()

		if (resp.success) {
			this.cIdFocus = resp.id
			this.say(adding ? '新建成功, 请继续新建。':'修改已经保存')
		} else {
			this.sayClear()
			this.errors = []
			for (var obj in resp.errors) {
				this.errors.push(eval('resp.errors.'+obj))
			}
			this.form.displayErrorMessages(this.errors)
			return
		}
		
		if (!adding)
			this.cancelEdit()
		else
			this.fillForm({})  //清空表单
		this.loadList()
	},
	//子类应重写此方法
	onLoadItem : Ext.emptyFn,
	//子类应重写此方法
	onSaveItem : Ext.emptyFn,
	//子类可以重写此方法，加入客户端验证算法
	validateItem : function(item) {
		return true
	},
	showDialog : function() {
		this.dialog.show();
	},
	hideDialog : function() {
		this.dialog.hide();
	},
	afterRender : function(ct, _2a2) {
		divo.panel.SmartGridDetailPanel.superclass.afterRender.call(this, ct, _2a2);
		this.onRenderOther();
	},
	onRenderOther : function() {
		this.createDialog();
	},
	createDialog : function() {
		this.dialog = new Ext.Window({
			title : this.dialogTitle,
			stateful : true,
			stateId : this.myId('detail-form'),
			modal : true,
			closable : true,
			closeAction : "hide",
			width : this.dialogWidth,
			height : this.dialogHeight,
			plain : true,
			layout : "fit",
			items : {
				margins : "3 3 3 0",
				autoScroll : true,
				items : this.form ? this.form : {}
			},
			buttons : [{
				text : "确定",
				handler : this.saveItem,
				scope : this
			}, {
				text : "取消",
				handler : this.cancelEdit,
				scope : this
			}]
		});
		this.dialog.render(Ext.getBody());
		if (this.form) {
			this.form.setItem({});
		}
		this.dialog.hide();
	},
	hideDialog : function() {
		this.dialog.hide();
	},
	toggleButtons : function(_2c7) {
		if (this.rendered) {
			this.getTopToolbar().showButtons(_2c7);
		}
	},
	beforeDestroy : function() {
		if (this.dialog) {
			Ext.destroy(this.dialog);
		}
		divo.panel.SmartGridDetailPanel.superclass.beforeDestroy
				.apply(this, arguments);
	}
	
});
//EOP
;//Thanks: http://extjs.com/forum/showthread.php?t=41357
if (Ext.isIE6) {
	Ext.util.CSS.createStyleSheet(".ux-panel-header-btns-ct-ie6 {float: right; width: 1px; }");
} 
if (Ext.isIE7) {
	Ext.util.CSS.createStyleSheet(".ux-panel-header-btns-ct-ie7 {float: right; width: 1px; }");
} 
Ext.namespace('Ext.ux.plugins');
Ext.ux.plugins.HeaderButtons = function(config)

{

	Ext.apply(this, config);

};

Ext.extend(Ext.ux.plugins.HeaderButtons, Ext.util.Observable,

{

	init : function(panel)

	{

		if (panel.hbuttons) {
			Ext.apply(panel, {
				onRender : panel.onRender
						.createSequence(function(ct, position) {
							if (this.headerButtons
									&& this.headerButtons.length > 0) {
								var tb = this.header.createChild({
									//cls : 'ux-panel-header-btns-ct',
									cls: (Ext.isIE7 || Ext.isIE6) ? 'ux-panel-header-btns-ct-ie'+(Ext.isIE6?'6':'7'): 'ux-panel-header-btns-ct',
									cn : {
										cls : "ux-panel-header-btns",
										html : '<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
									}
								}, this.header.first('span', true), true); // insert
																			// before
																			// header
																			// text
																			// (but
																			// after
																			// tools)

								var tr = tb.getElementsByTagName('tr')[0];
								for (var i = 0, len = this.headerButtons.length; i < len; i++) {
									var b = this.headerButtons[i];
									var td = document.createElement('td');
									td.className = 'ux-panel-header-btn-td';
									b.render(tr.appendChild(td));
								}
							}

						}),

				addHeaderButton : function(config, handler, scope) {
					var bc = {
						handler : handler,
						scope : scope,
						hideParent : true
					};
					if (typeof config == "string")
						bc.text = config;
					else
						Ext.apply(bc, config);

					var btn = new Ext.Button(bc);
					btn.ownerCt = this;
					if (!this.headerButtons)
						this.headerButtons = [];

					this.headerButtons.push(btn);
					return btn;
				}
			});

			var btns = panel.hbuttons;

			panel.headerButtons = [];
			for (var i = 0, len = btns.length; i < len; i++) {
				if (btns[i].render)
					panel.headerButtons.push(btns[i]);
				else
					panel.addHeaderButton(btns[i]);
			}

			delete panel.hbuttons;
		}
	}

});
// EOP
;Ext.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {
    lines:false,
    borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
    cls:'x-column-tree',
    
    onRender : function(){
        Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);
        this.headers = this.body.createChild(
            {cls:'x-tree-headers'},this.innerCt.dom);

        var cols = this.columns, c;
        var totalWidth = 0;

        for(var i = 0, len = cols.length; i < len; i++){
             c = cols[i];
             totalWidth += c.width;
             this.headers.createChild({
                 cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
                 cn: {
                     cls:'x-tree-hd-text',
                     html: c.header
                 },
                 style:'width:'+(c.width-this.borderWidth)+'px;'
             });
        }
        this.headers.createChild({cls:'x-clear'});
        // prevent floats from wrapping when clipped
        this.headers.setWidth(totalWidth);
        this.innerCt.setWidth(totalWidth);
    }
});
;Ext.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
    focus: Ext.emptyFn, // prevent odd scrolling behavior

    renderElements : function(n, a, targetNode, bulkRender){
        this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

        var t = n.getOwnerTree();
        var cols = t.columns;
        var bw = t.borderWidth;
        var c = cols[0];

        var buf = [
             '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',
                '<div class="x-tree-col" style="width:',c.width-bw,'px;">',
                    '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
                    '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',
                    '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',
                    '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',
                    a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',
                    '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",
                "</div>"];
         for(var i = 1, len = cols.length; i < len; i++){
             c = cols[i];

             buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
                        '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",
                      "</div>");
         }
         buf.push(
            '<div class="x-clear"></div></div>',
            '<ul class="x-tree-node-ct" style="display:none;"></ul>',
            "</li>");

        if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
            this.wrap = Ext.DomHelper.insertHtml("beforeBegin",
                                n.nextSibling.ui.getEl(), buf.join(""));
        }else{
            this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
        }

        this.elNode = this.wrap.childNodes[0];
        this.ctNode = this.wrap.childNodes[1];
        var cs = this.elNode.firstChild.childNodes;
        this.indentNode = cs[0];
        this.ecNode = cs[1];
        this.iconNode = cs[2];
        this.anchor = cs[3];
        this.textNode = cs[3].firstChild;
    }
});
;/**
 * 只读的动态加载树
 * 
 * --------------------------------------------------------------
 * 消息：
 * 用户选中了某个节点
 * 
 * 消息名：     			
 * j.tree.nodeSelect    
 * 
 * 消息内容：
 * {int} sender 本组件的id值
 * {Ext.tree.TreeNode} node 当前选中的节点对象
 * --------------------------------------------------------------
 */
divo.tree.SimpleTree = Ext.extend(Ext.tree.TreePanel, {
	url : null,
	nodeConfigFn : null,
	treeName : null,
	rootId : 1,
	initComponent : function() {
		Ext.apply(this, {
			animate : true,
			containerScroll : true,
			rootVisible : true,
			root : this.createRootNode(),
			tbar : this.getTbar(),
			autoScroll : true,
			loader : new Ext.tree.TreeLoader({
				requestMethod : 'GET',
				dataUrl : this.url,
				nodeConfigFn : this.nodeConfigFn,
				listeners : {
					"loadexception" : {
						fn : this.onLoadException,
						scope : this
					}
				}
			})
		})

		divo.tree.SimpleTree.superclass.initComponent.call(this)
	},
	render : function() {
		divo.tree.SimpleTree.superclass.render.apply(this, arguments);
		
		this.getSelectionModel().on('selectionchange', this.onNodeSelected, this, true)
		this.loader.on("load", this.onLoad,this)
	},	
	getTbar : function() {
		return ['->', {
			icon : divo.iconCollapseAll2,
			cls : 'x-btn-icon',
			tooltip : '全部收缩',
			handler : this.onCollapseAll,
			scope : this
		}]
	},
	// 处理加载异常
	onLoadException : function(loader, node, response) {
		divo.showError(response.responseText)
	},
	onCollapseAll : function() {
		this.root.collapse(true)
	},
	onNodeSelected : function(sm, node) {
		if (!node) return
		this.currentNodeId = node.id
		
		this.publish("j.tree.nodeSelect",{sender:this.id,node:node})
	},
	onLoad : function(o, node, response) {
		if (this.currentNodeId) {
			var currentNode = node.findChild("id", this.currentNodeId)
			if (currentNode) {
				currentNode.ensureVisible() // 自动滚动到该节点，并展开
				currentNode.expand()
				this.getSelectionModel().select(currentNode)
			} // 光标定位
		}
	},
	createRootNode : function() {
		var root
		Ext.Ajax.request({
			scope : this,
			url : this.rootUrl,
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (!resp.success) {
					this.say(resp.errors.reason)
					return
				}
				root = resp.data
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		if (!root) return null
		
		if (this.nodeConfigFn)
			var node = this.nodeConfigFn(root)
			
		Ext.apply(node,{
			draggable : false,
			expanded : false
		})
		return new Ext.tree.AsyncTreeNode(node)
	}

})
// EOP

;/**
 * 可以动态新建、修改和删除的树，还能拖动和调整节点顺序
 */
divo.tree.DynamicTree = Ext.extend(divo.tree.SimpleTree, {
	initComponent : function() {
		Ext.apply(this, {
			ddAppendOnly : true,
			autoScroll : true,
			enableDD : this.canUpdate()
		})	

		divo.tree.DynamicTree.superclass.initComponent.call(this)
	},
	render : function() {
		divo.tree.DynamicTree.superclass.render.apply(this, arguments);
		
		this.on("contextmenu", this.onShowContext, this)
		this.on('beforemovenode', this.onMoveNode, this)
	},	
	getTbar : function() {
		return [{
			text : '新建',
			tooltip : '新建' + this.treeName,
			icon : divo.iconAdd,
			cls : 'x-btn-text-icon',
			hidden : !this.canCreate(),
			handler : function() {
				var selectedNode = this.getSelectNode()
				if (!selectedNode) {
					return
				}
				this.onAdd(selectedNode)
			},
			scope : this
		}, '->', {
			icon : divo.iconCollapseAll2,
			cls : 'x-btn-icon',
			tooltip : '全部收缩',
			handler : this.onCollapseAll,
			scope : this
		}]
	},
	onShowContext : function(node, e) {
		this.createContextMenu()

		if (this.ctxNode) {
			this.ctxNode.ui.removeClass('x-node-ctx')
		}
		if (node) {
			this.ctxNode = node
			this.ctxNode.ui.addClass('x-node-ctx')
		}

		this.ctx.showAt(e.getXY())
	},
	createContextMenu : function() {
		if (this.ctx)
			return

		this.ctx = new Ext.menu.Menu({
			items : [{
				text : '修改',
				icon : divo.iconEdit,
				scope : this,
				hidden : !this.canUpdate(),
				handler : function() {
					var selectedNode = this.getSelectNode()
					if (!selectedNode) {
						return
					}
					this.onUpdate(selectedNode)
				}
			}, {
				text : '删除',
				icon : divo.iconDelete,
				scope : this,
				hidden : !this.canDelete(),
				handler : function() {
					var selectedNode = this.getSelectNode()
					if (!selectedNode) {
						return
					}
					this.onDelete(selectedNode)
				}
			}, '-', {
				text : '调整显示顺序',
				icon : divo.iconUrl + "/component.gif",
				scope : this,
				hidden : !this.canUpdate(),
				handler : function() {
					var selectedNode = this.getSelectNode()
					if (!selectedNode) {
						return
					}
					this.onChangeDisplayOrder(selectedNode)
				}
			}]
		});
		this.ctx.on('hide', this.onContextHide, this);
	},
	hasSelection : function() {
		var selectedNode = this.getSelectionModel().getSelectedNode()
		if (!selectedNode) {
			this.say('请先选择' + this.treeName + '。')
			return false
		}
		return true
	},
	getSelectNode : function() {
		var selectedNode
		if (this.ctxNode) {
			selectedNode = this.ctxNode
			this.ctxNode.select()
		} else {
			if (!this.hasSelection()) {
				return null
			} else {
				selectedNode = this.getSelectionModel().getSelectedNode()
			}
		}
		return selectedNode
	},
	onContextHide : function() {
		if (this.ctxNode) {
			this.ctxNode.ui.removeClass('x-node-ctx')
			this.ctxNode = null
		}
	},
	onMoveNode : function(tree, node, oldParent, newParent, index) {
		var allowMove = true

		// prevent other events while processing this one
		Ext.util.Observable.capture(tree, function() {
			return false
		})

		allowMove = this.onChangeNodeParent(node,oldParent,newParent)

		// restore normal event handling
		Ext.util.Observable.releaseCapture(tree)

		return allowMove
	},
	//以下方法必须由子类具体实现
	onAdd : Ext.emptyFn,
	onUpdate : Ext.emptyFn,
	onDelete : Ext.emptyFn,
	onChangeDisplayOrder : Ext.emptyFn,
	onChangeNodeParent : Ext.emptyFn

})
// EOP

;//Thanks: https://extjs.com/forum/showthread.php?t=32365
Ext.ux.ToastWindowMgr = {
    positions: [] 
};

Ext.ux.ToastWindow = Ext.extend(Ext.Window, {
    initComponent: function(){
          Ext.apply(this, {
            iconCls: this.iconCls || 'x-icon-information',
            width: 300,
            autoHeight: true,
            closable: false,
            plain: false,
            draggable: false,
            bodyStyle: 'text-align:center;padding:1em;'
          });
          if(this.autoDestroy) {
            this.task = new Ext.util.DelayedTask(this.hide, this);
          } else {
              this.closable = true;
          }
        Ext.ux.ToastWindow.superclass.initComponent.call(this);
    },
    setMessage: function(msg){
        this.body.update(msg);
    },
    setTitle: function(title, iconCls){
        Ext.ux.ToastWindow.superclass.setTitle.call(this, title, iconCls||this.iconCls);
    },
    onRender:function(ct, position) {
        Ext.ux.ToastWindow.superclass.onRender.call(this, ct, position);
    },
    onDestroy: function(){
        Ext.ux.ToastWindowMgr.positions.remove(this.pos);
        Ext.ux.ToastWindow.superclass.onDestroy.call(this);
    },
    afterShow: function(){
        Ext.ux.ToastWindow.superclass.afterShow.call(this);
        this.on('move', function(){
               Ext.ux.ToastWindowMgr.positions.remove(this.pos);
            this.task.cancel();}
        , this);
        if(this.autoDestroy) {
            this.task.delay(this.hideDelay || 5000);
       }
    },
    animShow: function(){
        this.pos = 0;
        while(Ext.ux.ToastWindowMgr.positions.indexOf(this.pos)>-1)
            this.pos++;
        Ext.ux.ToastWindowMgr.positions.push(this.pos);
        this.setSize(300,100);
        this.el.alignTo(document, "br-br", [ -20, -20-((this.getSize().height+10)*this.pos) ]);
        this.el.slideIn('b', {
            duration: 1,
            callback: this.afterShow,
            scope: this
        });    
    },
    animHide: function(){
        Ext.ux.ToastWindowMgr.positions.remove(this.pos);
        this.el.ghost("b", {
            duration: 1,
            remove: true
        });    
    }
}); 
//EOP
;/**
 * This namespace should be in another file but I dicided to put it here for consistancy.
 */
Ext.namespace('Ext.ux.Utils');

/**
 * This class implements event queue behaviour.
 *
 * @class Ext.ux.Utils.EventQueue
 * @param function  handler  Event handler.
 * @param object    scope    Handler scope.
 */
Ext.ux.Utils.EventQueue = function(handler, scope)
{
  if (!handler) {
    throw 'Handler is required.';
  }
  this.handler = handler;
  this.scope = scope || window;
  this.queue = [];
  this.is_processing = false;
  
  /**
   * Posts event into the queue.
   * 
   * @access public
   * @param mixed event Event identificator.
   * @param mixed data  Event data.
   */
  this.postEvent = function(event, data)
  {
    data = data || null;
    this.queue.push({event: event, data: data});
    if (!this.is_processing) {
      this.process();
    }
  }
  
  this.flushEventQueue = function()
  {
    this.queue = [];
  },
  
  /**
   * @access private
   */
  this.process = function()
  {
    while (this.queue.length > 0) {
      this.is_processing = true;
      var event_data = this.queue.shift();
      this.handler.call(this.scope, event_data.event, event_data.data);
    }
    this.is_processing = false;
  }
}

/**
 * This class implements Mili's finite state automata behaviour.
 *  
 *  Transition / output table format:
 *  {
 *    'state_1' : {
 *      'event_1' : [
 *        {
 *          p|predicate: function,    // Transition predicate, optional, default to true.
 *                                    // If array then conjunction will be applyed to the operands.
 *                                    // Predicate signature is (data, event, this).
 *          a|action: function|array, // Transition action, optional, default to Ext.emptyFn.
 *                                    // If array then methods will be called sequentially.
 *                                    // Action signature is (data, event, this).
 *          s|state: 'state_x',       // New state - transition destination, optional, default to 
 *                                    // current state.
 *          scope: object             // Predicate and action scope, optional, default to 
 *                                    // trans_table_scope or window.
 *        }
 *      ]
 *    },
 *
 *    'state_2' : {
 *      ...
 *    }
 *    ...
 *  }
 *
 *  @param  mixed initial_state Initial state.
 *  @param  object trans_table Transition / output table.
 *  @param  trans_table_scope Transition / output table's methods scope.
 */
Ext.ux.Utils.FSA = function(initial_state, trans_table, trans_table_scope)
{
  this.current_state = initial_state;
  this.trans_table = trans_table || {};
  this.trans_table_scope = trans_table_scope || window;
  Ext.ux.Utils.FSA.superclass.constructor.call(this, this.processEvent, this);
}

Ext.extend(Ext.ux.Utils.FSA, Ext.ux.Utils.EventQueue, {

  current_state : null,
  trans_table : null,  
  trans_table_scope : null,
  
  /**
   * Returns current state
   * 
   * @access public
   * @return mixed Current state.
   */
  state : function()
  {
    return this.current_state;
  },
  
  /**
   * @access public
   */
  processEvent : function(event, data)
  {
    var transitions = this.currentStateEventTransitions(event);
    if (!transitions) {
      throw "State '" + this.current_state + "' has no transition for event '" + event + "'.";
    }
    for (var i = 0, len = transitions.length; i < len; i++) {
      var transition = transitions[i];

      var predicate = transition.predicate || transition.p || true;
      var action = transition.action || transition.a || Ext.emptyFn;
      var new_state = transition.state || transition.s || this.current_state;
      var scope = transition.scope || this.trans_table_scope;
      
      if (this.computePredicate(predicate, scope, data, event)) {
        this.callAction(action, scope, data, event);
        this.current_state = new_state; 
        return;
      }
    }
    
    throw "State '" + this.current_state + "' has no transition for event '" + event + "' in current context";
  },
  
  /**
   * @access private
   */
  currentStateEventTransitions : function(event)
  {
    return this.trans_table[this.current_state] ? 
      this.trans_table[this.current_state][event] || false
      :
      false;
  },
  
  /**
   * @access private
   */
  computePredicate : function(predicate, scope, data, event)
  {
    var result = false; 
    
    switch (Ext.type(predicate)) {
     case 'function':
       result = predicate.call(scope, data, event, this);
       break;
     case 'array':
       result = true;
       for (var i = 0, len = predicate.length; result && (i < len); i++) {
         if (Ext.type(predicate[i]) == 'function') {
           result = predicate[i].call(scope, data, event, this);
         }
         else {
           throw [
             'Predicate: ',
             predicate[i],
             ' is not callable in "',
             this.current_state,
             '" state for event "',
             event
           ].join('');
         }
       }
       break;
     case 'boolean':
       result = predicate;
       break;
     default:
       throw [
         'Predicate: ',
         predicate,
         ' is not callable in "',
         this.current_state,
         '" state for event "',
         event
       ].join('');
    }
    return result;
  },
  
  /**
   * @access private
   */
  callAction : function(action, scope, data, event)
  {
    switch (Ext.type(action)) {
       case 'array':
       for (var i = 0, len = action.length; i < len; i++) {
         if (Ext.type(action[i]) == 'function') {
           action[i].call(scope, data, event, this);
         }
         else {
           throw [
             'Action: ',
             action[i],
             ' is not callable in "',
             this.current_state,
             '" state for event "',
             event
           ].join('');
         }
       }
         break;
     case 'function':
       action.call(scope, data, event, this);
       break;
     default:
       throw [
         'Action: ',
         action,
         ' is not callable in "',
         this.current_state,
         '" state for event "',
         event
       ].join('');
    }
  }
});

// ---------------------------------------------------------------------------------------------- //

/**
 * Ext.ux.UploadDialog namespace.
 */
Ext.namespace('Ext.ux.UploadDialog');

/**
 * File upload browse button.
 *
 * @class Ext.ux.UploadDialog.BrowseButton
 */ 
Ext.ux.UploadDialog.BrowseButton = Ext.extend(Ext.Button, 
{
  input_name : 'file',
  
  input_file : null,
  
  original_handler : null,
  
  original_scope : null,
  
  /**
   * @access private
   */
  initComponent : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.initComponent.call(this);
    this.original_handler = this.handler || null;
    this.original_scope = this.scope || window;
    this.handler = null;
    this.scope = null;
  },
  
  /**
   * @access private
   */
  onRender : function(ct, position)
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.onRender.call(this, ct, position);
    this.createInputFile();
  },
  
  /**
   * @access private
   */
  createInputFile : function()
  {
    var button_container = this.el.child('.x-btn-center');
    button_container.position('relative');
    this.input_file = Ext.DomHelper.append(
      button_container, 
      {
        tag: 'input',
        type: 'file',
        size: 1,
        name: this.input_name || Ext.id(this.el),
        style: 'position: absolute; display: block; border: none; cursor: pointer'
      },
      true
    );
    
    var button_box = button_container.getBox();
    this.input_file.setStyle('font-size', (button_box.width * 0.5) + 'px');

    var input_box = this.input_file.getBox();
    var adj = {x: 3, y: 3}
    if (Ext.isIE) {
      adj = {x: 0, y: 3}
    }
    
    this.input_file.setLeft(button_box.width - input_box.width + adj.x + 'px');
    this.input_file.setTop(button_box.height - input_box.height + adj.y + 'px');
    this.input_file.setOpacity(0.0);
        
    if (this.handleMouseEvents) {
      this.input_file.on('mouseover', this.onMouseOver, this);
        this.input_file.on('mousedown', this.onMouseDown, this);
    }
    
    if(this.tooltip){
      if(typeof this.tooltip == 'object'){
        Ext.QuickTips.register(Ext.apply({target: this.input_file}, this.tooltip));
      } 
      else {
        this.input_file.dom[this.tooltipType] = this.tooltip;
        }
      }
    
    this.input_file.on('change', this.onInputFileChange, this);
    this.input_file.on('click', function(e) { e.stopPropagation(); }); 
  },
  
  /**
   * @access public
   */
  detachInputFile : function(no_create)
  {
    var result = this.input_file;
    
    no_create = no_create || false;
    
    if (typeof this.tooltip == 'object') {
      Ext.QuickTips.unregister(this.input_file);
    }
    else {
      this.input_file.dom[this.tooltipType] = null;
    }
    this.input_file.removeAllListeners();
    this.input_file = null;
    
    if (!no_create) {
      this.createInputFile();
    }
    return result;
  },
  
  /**
   * @access public
   */
  getInputFile : function()
  {
    return this.input_file;
  },
  
  /**
   * @access public
   */
  disable : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.disable.call(this);  
    this.input_file.dom.disabled = true;
  },
  
  /**
   * @access public
   */
  enable : function()
  {
    Ext.ux.UploadDialog.BrowseButton.superclass.enable.call(this);
    this.input_file.dom.disabled = false;
  },
  
  /**
   * @access public
   */
  destroy : function()
  {
    var input_file = this.detachInputFile(true);
    input_file.remove();
    input_file = null;
    Ext.ux.UploadDialog.BrowseButton.superclass.destroy.call(this);      
  },
  
  /**
   * @access private
   */
  onInputFileChange : function()
  {
    if (this.original_handler) {
      this.original_handler.call(this.original_scope, this);
    }
  }  
});

/**
 * Toolbar file upload browse button.
 *
 * @class Ext.ux.UploadDialog.TBBrowseButton
 */
Ext.ux.UploadDialog.TBBrowseButton = Ext.extend(Ext.ux.UploadDialog.BrowseButton, 
{
  hideParent : true,

  onDestroy : function()
  {
    Ext.ux.UploadDialog.TBBrowseButton.superclass.onDestroy.call(this);
    if(this.container) {
      this.container.remove();
      }
  }
});

/**
 * Record type for dialogs grid.
 *
 * @class Ext.ux.UploadDialog.FileRecord 
 */
Ext.ux.UploadDialog.FileRecord = Ext.data.Record.create([
  {name: 'filename'},
  {name: 'state', type: 'int'},
  {name: 'note'},
  {name: 'input_element'}
]);

Ext.ux.UploadDialog.FileRecord.STATE_QUEUE = 0;
Ext.ux.UploadDialog.FileRecord.STATE_FINISHED = 1;
Ext.ux.UploadDialog.FileRecord.STATE_FAILED = 2;
Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING = 3;

/**
 * Dialog class.
 *
 * @class Ext.ux.UploadDialog.Dialog
 */
Ext.ux.UploadDialog.Dialog = function(config)
{
  var default_config = {
    border: false,
    width: 450,
    height: 300,
    minWidth: 450,
    minHeight: 300,
    modal : true,
    plain: true,
    constrainHeader: true,
    draggable: true,
    closable: true,
    maximizable: false,
    minimizable: false,
    resizable: true,
    autoDestroy: true,
    closeAction: 'hide',
    title: this.i18n.title,
    cls: 'ext-ux-uploaddialog-dialog',
    // --------
    url: '',
    base_params: {},
    permitted_extensions: [],
    reset_on_hide: true,
    allow_close_on_upload: false,
    upload_autostart: false,
    post_var_name: 'file'
  }
  config = Ext.applyIf(config || {}, default_config);
  config.layout = 'absolute';
  
  Ext.ux.UploadDialog.Dialog.superclass.constructor.call(this, config);
}

Ext.extend(Ext.ux.UploadDialog.Dialog, Ext.Window, {

  fsa : null,
  
  state_tpl : null,
  
  form : null,
  
  grid_panel : null,
  
  progress_bar : null,
  
  is_uploading : false,
  
  initial_queued_count : 0,
  
  upload_frame : null,
  
  /**
   * @access private
   */
  //--------------------------------------------------------------------------------------------- //
  initComponent : function()
  {
    Ext.ux.UploadDialog.Dialog.superclass.initComponent.call(this);
    
    // Setting automata protocol
    var tt = {
      // --------------
      'created' : {
      // --------------
        'window-render' : [
          {
            action: [this.createForm, this.createProgressBar, this.createGrid],
            state: 'rendering'
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'rendering' : {
      // --------------
        'grid-render' : [
          {
            action: [this.fillToolbar, this.updateToolbar],
            state: 'ready'
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'ready' : {
      // --------------
        'file-selected' : [
          {
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
            action: this.addFileToUploadQueue,
            state: 'adding-file'
          },
          {
            // If file is not permitted then do nothing.
          }
        ],
        'grid-selection-change' : [
          {
            action: this.updateToolbar
          }
        ],
        'remove-files' : [
          {
            action: [this.removeFiles, this.fireFileRemoveEvent]
          }
        ],
        'reset-queue' : [
          {
            action: [this.resetQueue, this.fireResetQueueEvent]
          }
        ],
        'start-upload' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.setUploadingFlag, this.saveInitialQueuedCount, this.updateToolbar, 
              this.updateProgressBar, this.prepareNextUploadTask, this.fireUploadStartEvent
            ],
            state: 'uploading'
          },
          {
            // Has nothing to upload, do nothing.
          }
        ],
        'stop-upload' : [
          {
            // We are not uploading, do nothing. Can be posted by user only at this state. 
          }
        ],
        'hide' : [
          {
            predicate: [this.isNotEmptyQueue, this.getResetOnHide],
            action: [this.resetQueue, this.fireResetQueueEvent]
          },
          {
            // Do nothing
          }
        ],
        'destroy' : [
          {
            action: this.flushEventQueue,
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'adding-file' : {
      // --------------
        'file-added' : [
          {
            predicate: this.isUploading,
            action: [this.incInitialQueuedCount, this.updateProgressBar, this.fireFileAddEvent],
            state: 'uploading' 
          },
          {
            predicate: this.getUploadAutostart,
            action: [this.startUpload, this.fireFileAddEvent],
            state: 'ready'
          },
          {
            action: [this.updateToolbar, this.fireFileAddEvent],
            state: 'ready'
          }
        ]
      },
      // --------------
      'uploading' : {
      // --------------
        'file-selected' : [
          {
            predicate: [this.fireFileTestEvent, this.isPermittedFile],
            action: this.addFileToUploadQueue,
            state: 'adding-file'
          },
          {
            // If file is not permitted then do nothing.
          }
        ],
        'grid-selection-change' : [
          {
            // Do nothing.
          }
        ],
        'start-upload' : [
          {
            // Can be posted only by user in this state. 
          }
        ],
        'stop-upload' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar, 
              this.updateProgressBar, this.fireUploadStopEvent
            ],
            state: 'ready'
          },
          {
            action: [
              this.resetUploadingFlag, this.abortUpload, this.updateToolbar, 
              this.updateProgressBar, this.fireUploadStopEvent, this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-start' : [
          {
            action: [this.uploadFile, this.findUploadFrame, this.fireFileUploadStartEvent]
          }
        ],
        'file-upload-success' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadSuccessEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadSuccessEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-error' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadErrorEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadErrorEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'file-upload-failed' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadFrame, this.updateRecordState, this.updateProgressBar, 
              this.prepareNextUploadTask, this.fireUploadFailedEvent
            ]
          },
          {
            action: [
              this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, 
              this.updateToolbar, this.updateProgressBar, this.fireUploadFailedEvent, 
              this.fireUploadCompleteEvent
            ],
            state: 'ready'
          }
        ],
        'hide' : [
          {
            predicate: this.getResetOnHide,
            action: [this.stopUpload, this.repostHide]
          },
          {
            // Do nothing.
          }
        ],
        'destroy' : [
          {
            predicate: this.hasUnuploadedFiles,
            action: [
              this.resetUploadingFlag, this.abortUpload,
              this.fireUploadStopEvent, this.flushEventQueue
            ],
            state: 'destroyed'
          },
          {
            action: [
              this.resetUploadingFlag, this.abortUpload,
              this.fireUploadStopEvent, this.fireUploadCompleteEvent, this.flushEventQueue
            ], 
            state: 'destroyed'
          }
        ]
      },
      // --------------
      'destroyed' : {
      // --------------
      }
    }
    this.fsa = new Ext.ux.Utils.FSA('created', tt, this);
    
    // Registering dialog events.
    this.addEvents({
      'filetest': true,
      'fileadd' : true,
      'fileremove' : true,
      'resetqueue' : true,
      'uploadsuccess' : true,
      'uploaderror' : true,
      'uploadfailed' : true,
      'uploadstart' : true,
      'uploadstop' : true,
      'uploadcomplete' : true,
      'fileuploadstart' : true
    });
    
    // Attaching to window events.
    this.on('render', this.onWindowRender, this);
    this.on('beforehide', this.onWindowBeforeHide, this);
    this.on('hide', this.onWindowHide, this);
    this.on('destroy', this.onWindowDestroy, this);
    
    // Compiling state template.
    this.state_tpl = new Ext.Template(
      "<div class='ext-ux-uploaddialog-state ext-ux-uploaddialog-state-{state}'>&#160;</div>"
    ).compile();
  },
  
  createForm : function()
  {
    this.form = Ext.DomHelper.append(this.body, {
      tag: 'form',
      method: 'post',
      action: this.url,
      style: 'position: absolute; left: -100px; top: -100px; width: 100px; height: 100px'
    });
  },
  
  createProgressBar : function()
  {
    this.progress_bar = this.add(
      new Ext.ProgressBar({
        x: 0,
        y: 0,
        anchor: '0',
        value: 0.0,
        text: this.i18n.progress_waiting_text
      })
    );
  },
  
  createGrid : function()
  {
    var store = new Ext.data.Store({
      proxy: new Ext.data.MemoryProxy([]),
      reader: new Ext.data.JsonReader({}, Ext.ux.UploadDialog.FileRecord),
      sortInfo: {field: 'state', direction: 'DESC'},
      pruneModifiedRecords: true
    });
    
    var cm = new Ext.grid.ColumnModel([
      {
        header: this.i18n.state_col_title,
        width: this.i18n.state_col_width,
        resizable: false,
        dataIndex: 'state',
        sortable: true,
        renderer: this.renderStateCell.createDelegate(this)
      },
      {
        header: this.i18n.filename_col_title,
        width: this.i18n.filename_col_width,
        dataIndex: 'filename',
        sortable: true,
        renderer: this.renderFilenameCell.createDelegate(this)
      },
      {
        header: this.i18n.note_col_title,
        width: this.i18n.note_col_width, 
        dataIndex: 'note',
        sortable: true,
        renderer: this.renderNoteCell.createDelegate(this)
      }
    ]);
  
      this.grid_panel = new Ext.grid.GridPanel({
      ds: store,
      cm: cm,
    
      x: 0,
      y: 22,
      anchor: '0 -22',
      border: true,
      
        viewConfig: {
        autoFill: true,
          forceFit: true
        },
      
      bbar : new Ext.Toolbar()
    });
    this.grid_panel.on('render', this.onGridRender, this);
    
    this.add(this.grid_panel);
    
    this.grid_panel.getSelectionModel().on('selectionchange', this.onGridSelectionChange, this);
  },
  
  fillToolbar : function()
  {
    var tb = this.grid_panel.getBottomToolbar();
    tb.x_buttons = {}
    
    tb.x_buttons.add = tb.addItem(new Ext.ux.UploadDialog.TBBrowseButton({
      input_name: this.post_var_name,
      text: this.i18n.add_btn_text,
      tooltip: this.i18n.add_btn_tip,
      iconCls: 'ext-ux-uploaddialog-addbtn',
      handler: this.onAddButtonFileSelected,
      scope: this
    }));
    
    tb.x_buttons.remove = tb.addButton({
      text: this.i18n.remove_btn_text,
      tooltip: this.i18n.remove_btn_tip,
      iconCls: 'ext-ux-uploaddialog-removebtn',
      handler: this.onRemoveButtonClick,
      scope: this
    });
    
    tb.x_buttons.reset = tb.addButton({
      text: this.i18n.reset_btn_text,
      tooltip: this.i18n.reset_btn_tip,
      iconCls: 'ext-ux-uploaddialog-resetbtn',
      handler: this.onResetButtonClick,
      scope: this
    });
    
    tb.add('-');
    
    tb.x_buttons.upload = tb.addButton({
      text: this.i18n.upload_btn_start_text,
      tooltip: this.i18n.upload_btn_start_tip,
      iconCls: 'ext-ux-uploaddialog-uploadstartbtn',
      handler: this.onUploadButtonClick,
      scope: this
    });
    
    tb.add('-');
    
    tb.x_buttons.indicator = tb.addItem(
      new Ext.Toolbar.Item(
        Ext.DomHelper.append(tb.getEl(), {
          tag: 'div',
          cls: 'ext-ux-uploaddialog-indicator-stoped',
          html: '&#160'
        })
      )
    );
    
    tb.add('->');
    
    tb.x_buttons.close = tb.addButton({
      text: this.i18n.close_btn_text,
      tooltip: this.i18n.close_btn_tip,
      handler: this.onCloseButtonClick,
      scope: this
    });
  },
  
  renderStateCell : function(data, cell, record, row_index, column_index, store)
  {
    return this.state_tpl.apply({state: data});
  },
  
  renderFilenameCell : function(data, cell, record, row_index, column_index, store)
  {
    var view = this.grid_panel.getView();
    var f = function() {
      try {
        Ext.fly(
          view.getCell(row_index, column_index)
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
      }
      catch (e)
      {}
    }
    f.defer(1000);
    return data;
  },
  
  renderNoteCell : function(data, cell, record, row_index, column_index, store)
  {
    var view = this.grid_panel.getView();
    var f = function() {
      try {
        Ext.fly(
          view.getCell(row_index, column_index)
        ).child('.x-grid3-cell-inner').dom['qtip'] = data;
      }
      catch (e)
      {}
      }
    f.defer(1000);
    return data;
  },
  
  getFileExtension : function(filename)
  {
    var result = null;
    var parts = filename.split('.');
    if (parts.length > 1) {
      result = parts.pop();
    }
    return result;
  },
  
  isPermittedFileType : function(filename)
  {
    var result = true;
    if (this.permitted_extensions.length > 0) {
      result = this.permitted_extensions.indexOf(this.getFileExtension(filename)) != -1;
    }
    return result;
  },

  isPermittedFile : function(browse_btn)
  {
    var result = false;
    var filename = browse_btn.getInputFile().dom.value;
    
    if (this.isPermittedFileType(filename)) {
      result = true;
    }
    else {
      Ext.Msg.alert(
        this.i18n.error_msgbox_title, 
        String.format(
          this.i18n.err_file_type_not_permitted,
          filename,
          this.permitted_extensions.join(this.i18n.permitted_extensions_join_str)
        )
      );
      result = false;
    }
    
    return result;
  },
  
  fireFileTestEvent : function(browse_btn)
  {
    return this.fireEvent('filetest', this, browse_btn.getInputFile().dom.value) !== false;
  },
  
  addFileToUploadQueue : function(browse_btn)
  {
    var input_file = browse_btn.detachInputFile();
    
    input_file.appendTo(this.form);
    input_file.setStyle('width', '100px');
    input_file.dom.disabled = true;
    
    var store = this.grid_panel.getStore();
    store.add(
      new Ext.ux.UploadDialog.FileRecord({
          state: Ext.ux.UploadDialog.FileRecord.STATE_QUEUE,
          filename: input_file.dom.value,
          note: this.i18n.note_queued_to_upload,
          input_element: input_file
        })
      );
    this.fsa.postEvent('file-added', input_file.dom.value);
  },
  
  fireFileAddEvent : function(filename)
  {
    this.fireEvent('fileadd', this, filename);
  },
  
  updateProgressBar : function()
  {
    if (this.is_uploading) {
      var queued = this.getQueuedCount(true);
      var value = 1 - queued / this.initial_queued_count;
      this.progress_bar.updateProgress(
        value,
        String.format(
          this.i18n.progress_uploading_text, 
          this.initial_queued_count - queued,
          this.initial_queued_count
        )
      );
    }
    else {
      this.progress_bar.updateProgress(0, this.i18n.progress_waiting_text);
    }
  },
  
  updateToolbar : function()
  {
    var tb = this.grid_panel.getBottomToolbar();
    if (this.is_uploading) {
      tb.x_buttons.remove.disable();
      tb.x_buttons.reset.disable();
      tb.x_buttons.upload.enable();
      if (!this.getAllowCloseOnUpload()) {
        tb.x_buttons.close.disable();
      }
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
        'ext-ux-uploaddialog-indicator-stoped',
        'ext-ux-uploaddialog-indicator-processing'
      );
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstopbtn');
      tb.x_buttons.upload.setText(this.i18n.upload_btn_stop_text);
      tb.x_buttons.upload.getEl()
        .child(tb.x_buttons.upload.buttonSelector)
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_stop_tip;
    }
    else {
      tb.x_buttons.remove.enable();
      tb.x_buttons.reset.enable();
      tb.x_buttons.close.enable();
      Ext.fly(tb.x_buttons.indicator.getEl()).replaceClass(
        'ext-ux-uploaddialog-indicator-processing',
        'ext-ux-uploaddialog-indicator-stoped'
      );
      tb.x_buttons.upload.setIconClass('ext-ux-uploaddialog-uploadstartbtn');
      tb.x_buttons.upload.setText(this.i18n.upload_btn_start_text);
      tb.x_buttons.upload.getEl()
        .child(tb.x_buttons.upload.buttonSelector)
        .dom[tb.x_buttons.upload.tooltipType] = this.i18n.upload_btn_start_tip;
      
      if (this.getQueuedCount() > 0) {
        tb.x_buttons.upload.enable();
      }
      else {
        tb.x_buttons.upload.disable();      
      }
      
      if (this.grid_panel.getSelectionModel().hasSelection()) {
        tb.x_buttons.remove.enable();
      }
      else {
        tb.x_buttons.remove.disable();
      }
      
      if (this.grid_panel.getStore().getCount() > 0) {
        tb.x_buttons.reset.enable();
      }
      else {
        tb.x_buttons.reset.disable();
      }
    }
  },
  
  saveInitialQueuedCount : function()
  {
    this.initial_queued_count = this.getQueuedCount();
  },
  
  incInitialQueuedCount : function()
  {
    this.initial_queued_count++;
  },
  
  setUploadingFlag : function()
  {
    this.is_uploading = true;
  }, 
  
  resetUploadingFlag : function()
  {
    this.is_uploading = false;
  },

  prepareNextUploadTask : function()
  {
    // Searching for first unuploaded file.
    var store = this.grid_panel.getStore();
    var record = null;
    
    store.each(function(r) {
      if (!record && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
        record = r;
      }
      else {
        r.get('input_element').dom.disabled = true;
      }
    });
    
    record.get('input_element').dom.disabled = false;
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING);
    record.set('note', this.i18n.note_processing);
    record.commit();
    
    this.fsa.postEvent('file-upload-start', record);
  },
   
  fireUploadStartEvent : function()
  {
    this.fireEvent('uploadstart', this);
  },
  
  removeFiles : function(file_records)
  {
    var store = this.grid_panel.getStore();
    for (var i = 0, len = file_records.length; i < len; i++) {
      var r = file_records[i];
      r.get('input_element').remove();
      store.remove(r);
    }
  },
  
  fireFileRemoveEvent : function(file_records)
  {
    for (var i = 0, len = file_records.length; i < len; i++) {
      this.fireEvent('fileremove', this, file_records[i].get('filename'));
    }
  },
  
  resetQueue : function()
  {
    var store = this.grid_panel.getStore();
    store.each(
      function(r) {
        r.get('input_element').remove();
      }
    );
    store.removeAll();
  },
  
  fireResetQueueEvent : function()
  {
    this.fireEvent('resetqueue', this);
  },
  
  uploadFile : function(record)
  {
    Ext.Ajax.request({
      url : this.url,
      params : this.base_params || this.baseParams || this.params,
      method : 'POST',
      form : this.form,
      isUpload : true,
      success : this.onAjaxSuccess,
      failure : this.onAjaxFailure,
      scope : this,
      record: record
    });
  },
   
  fireFileUploadStartEvent : function(record)
  {
    this.fireEvent('fileuploadstart', this, record.get('filename'));
  },
  
  updateRecordState : function(data)
  {
    if ('success' in data.response && data.response.success) {
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FINISHED);
      data.record.set(
        'note', this.i18n.note_upload_success
      );
    }
    else {
      data.record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
      data.record.set(
        'note', data.response.message || data.response.error || this.i18n.note_upload_error
      );
    }
    
    data.record.commit();
  },
  
  fireUploadSuccessEvent : function(data)
  {
    this.fireEvent('uploadsuccess', this, data.record.get('filename'), data.response);
  },
  
  fireUploadErrorEvent : function(data)
  {
    this.fireEvent('uploaderror', this, data.record.get('filename'), data.response);
  },
  
  fireUploadFailedEvent : function(data)
  {
    this.fireEvent('uploadfailed', this, data.record.get('filename'));
  },
  
  fireUploadCompleteEvent : function()
  {
    this.fireEvent('uploadcomplete', this);
  },
  
  findUploadFrame : function() 
  {
    this.upload_frame = Ext.getBody().child('iframe.x-hidden:last');
  },
  
  resetUploadFrame : function()
  {
    this.upload_frame = null;
  },
  
  removeUploadFrame : function()
  {
    if (this.upload_frame) {
      this.upload_frame.removeAllListeners();
      this.upload_frame.dom.src = 'about:blank';
      this.upload_frame.remove();
    }
    this.upload_frame = null;
  },
  
  abortUpload : function()
  {
    this.removeUploadFrame();
    
    var store = this.grid_panel.getStore();
    var record = null;
    store.each(function(r) {
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
        record = r;
        return false;
      }
    });
    
    record.set('state', Ext.ux.UploadDialog.FileRecord.STATE_FAILED);
    record.set('note', this.i18n.note_aborted);
    record.commit();
  },
  
  fireUploadStopEvent : function()
  {
    this.fireEvent('uploadstop', this);
  },
  
  repostHide : function()
  {
    this.fsa.postEvent('hide');
  },
  
  flushEventQueue : function()
  {
    this.fsa.flushEventQueue();
  },
  
  /**
   * @access private
   */
  // -------------------------------------------------------------------------------------------- //
  onWindowRender : function()
  {
    this.fsa.postEvent('window-render');
  },
  
  onWindowBeforeHide : function()
  {
    return this.isUploading() ? this.getAllowCloseOnUpload() : true;
  },
  
  onWindowHide : function()
  {
    this.fsa.postEvent('hide');
  },
  
  onWindowDestroy : function()
  {
    this.fsa.postEvent('destroy');
  },
  
  onGridRender : function()
  {
    this.fsa.postEvent('grid-render');
  },
  
  onGridSelectionChange : function()
  {
    this.fsa.postEvent('grid-selection-change');
  },
  
  onAddButtonFileSelected : function(btn)
  {
    this.fsa.postEvent('file-selected', btn);
  },
  
  onUploadButtonClick : function()
  {
    if (this.is_uploading) {
      this.fsa.postEvent('stop-upload');
    }
    else {
      this.fsa.postEvent('start-upload');
    }
  },
  
  onRemoveButtonClick : function()
  {
    var selections = this.grid_panel.getSelectionModel().getSelections();
    this.fsa.postEvent('remove-files', selections);
  },
  
  onResetButtonClick : function()
  {
    this.fsa.postEvent('reset-queue');
  },
  
  onCloseButtonClick : function()
  {
    this[this.closeAction].call(this);
  },
  
  onAjaxSuccess : function(response, options)
  {
    var json_response = {
      'success' : false,
      'error' : this.i18n.note_upload_error
    }
    try { 
        var rt = response.responseText;
        var filter = rt.match(/^<[^>]+>((?:.|\n)*)<\/[^>]+>$/);
        if (filter) {
            rt = filter[1];
        }
        json_response = Ext.util.JSON.decode(rt); 
    } 
    catch (e) {}
    
    var data = {
      record: options.record,
      response: json_response
    }
    
    if ('success' in json_response) {
      this.fsa.postEvent('file-upload-success', data);
    }
    else {
      this.fsa.postEvent('file-upload-error', data);
    }
  },
  
  onAjaxFailure : function(response, options)
  {
    var data = {
      record : options.record,
      response : {
        'success' : false,
        'error' : this.i18n.note_upload_failed
      }
    }

    this.fsa.postEvent('file-upload-failed', data);
  },
  
  /**
   * @access public
   */
  // -------------------------------------------------------------------------------------------- //
  startUpload : function()
  {
    this.fsa.postEvent('start-upload');
  },
  
  stopUpload : function()
  {
    this.fsa.postEvent('stop-upload');
  },
  
  getUrl : function()
  {
    return this.url;
  },
  
  setUrl : function(url)
  {
    this.url = url;
  },
  
  getBaseParams : function()
  {
    return this.base_params;
  },
  
  setBaseParams : function(params)
  {
    this.base_params = params;
  },
  
  getUploadAutostart : function()
  {
    return this.upload_autostart;
  },
  
  setUploadAutostart : function(value)
  {
    this.upload_autostart = value;
  },
  
  getAllowCloseOnUpload : function()
  {
    return this.allow_close_on_upload;
  },
  
  setAllowCloseOnUpload : function(value)
  {
    this.allow_close_on_upload = value;
  },
  
  getResetOnHide : function()
  {
    return this.reset_on_hide;
  },
  
  setResetOnHide : function(value)
  {
    this.reset_on_hide = value;
  },
  
  getPermittedExtensions : function()
  {
    return this.permitted_extensions;
  },
  
  setPermittedExtensions : function(value)
  {
    this.permitted_extensions = value;
  },
  
  isUploading : function()
  {
    return this.is_uploading;
  },
  
  isNotEmptyQueue : function()
  {
    return this.grid_panel.getStore().getCount() > 0;
  },
  
  getQueuedCount : function(count_processing)
  {
    var count = 0;
    var store = this.grid_panel.getStore();
    store.each(function(r) {
      if (r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_QUEUE) {
        count++;
      }
      if (count_processing && r.get('state') == Ext.ux.UploadDialog.FileRecord.STATE_PROCESSING) {
        count++;
      }
    });
    return count;
  },
  
  hasUnuploadedFiles : function()
  {
    return this.getQueuedCount() > 0;
  }
});

// ---------------------------------------------------------------------------------------------- //

var p = Ext.ux.UploadDialog.Dialog.prototype;
p.i18n = {
  title: '文件上传',
  state_col_title: '状态',
  state_col_width: 70,
  filename_col_title: '文件名',
  filename_col_width: 230,  
  note_col_title: '备注',
  note_col_width: 150,
  add_btn_text: '添加',
  add_btn_tip: '添加文件到上传队列.',
  remove_btn_text: '删除',
  remove_btn_tip: '从上传队列中删除文件.',
  reset_btn_text: '重置',
  reset_btn_tip: '重置队列.',
  upload_btn_start_text: '上传',
  upload_btn_stop_text: '放弃',
  upload_btn_start_tip: '上传队列中的文件到服务器.',
  upload_btn_stop_tip: '停止上传.',
  close_btn_text: '关闭',
  close_btn_tip: '关闭此窗口.',
  progress_waiting_text: '(空闲)',
  progress_uploading_text: '正在上传: 已经完成 {0} / {1} .',
  error_msgbox_title: '错误',
  permitted_extensions_join_str: ',',
  err_file_type_not_permitted: '所选文件的扩展名无效.<br/>请选择下列扩展名的文件: {1}',
  note_queued_to_upload: '上传排队中',
  note_processing: '正在上传...',
  note_upload_failed: '服务器故障.',
  note_upload_success: '上传成功.',
  note_upload_error: '上传失败.',
  note_aborted: '被用户放弃.'
};/**
 * Thanks: http://extjs.com/forum/showthread.php?t=20662
 */
Ext.ux.MaximizeTool = function() {
	this.init = function(ct) {
		var maximizeTool = {
			id : 'maximize',
			handler : handleMaximize,
			scope : ct,
			qtip : '最大化'
		};
		ct.tools = ct.tools || [];
		var newTools = ct.tools.slice();
		ct.tools = newTools;
		for (var i = 0, len = ct.tools.length;i < len; i++) {
			if (ct.tools[i].id == 'maximize') {
				return;
			}
		} //避免重复添加
		var hasAdded = false
		for (var i = 0, len = ct.tools.length;i < len; i++) {
			if (ct.tools[i].id == 'close') {
				ct.tools[ct.tools.length] = ct.tools[i]
				ct.tools[i] = maximizeTool
				hasAdded = true
				break
			} //让close在最后
		}
		if (!hasAdded)
		    ct.tools[ct.tools.length] = maximizeTool //放在最后
	}

	function handleMaximize(event, toolEl, panel) {
		panel.originalOwnerCt = panel.ownerCt;
		panel.originalPosition = panel.ownerCt.items.indexOf(panel);
		panel.originalSize = panel.getSize();

		if (!toolEl.window) {
			var defaultConfig = {
				id : (panel.getId() + '-MAX'),
				width : (Ext.getBody().getSize().width - 100),
				height : (Ext.getBody().getSize().height - 100),
				resizable : true,
				draggable : true,
				closable : true,
				closeAction : 'hide',
				hideBorders : true,
				plain : true,
				layout : 'fit',
				autoScroll : false,
				border : false,
				bodyBorder : false,
				frame : true,
				pinned : true,
				bodyStyle : 'background-color: #ffffff;'
			};
			toolEl.window = new Ext.Window(defaultConfig);
			toolEl.window.on('hide', handleMinimize, panel);
		}
		if (!panel.dummyComponent) {
			var dummyCompConfig = {
				title : panel.title,
				width : panel.getSize().width,
				height : panel.getSize().height,
				html : '&nbsp;'
			};
			panel.dummyComponent = new Ext.Panel(dummyCompConfig);
		}

		toolEl.window.add(panel);
		if (panel.tools['toggle'])
			panel.tools['toggle'].setVisible(false);
		if (panel.tools['close'])
			panel.tools['close'].setVisible(false);
		panel.tools['maximize'].setVisible(false);

		panel.originalOwnerCt.insert(panel.originalPosition,
				panel.dummyComponent);
		panel.originalOwnerCt.doLayout();
		panel.dummyComponent.setSize(panel.originalSize);
		panel.dummyComponent.setVisible(true);
		panel.dummyComponent.getEl().mask('已被最大化');
		toolEl.window.show(this);
	};

	function handleMinimize(window) {
		this.dummyComponent.getEl().unmask();
		this.dummyComponent.setVisible(false);
		this.originalOwnerCt.insert(this.originalPosition, this);
		this.originalOwnerCt.doLayout();
		this.setSize(this.originalSize);
		this.tools['maximize'].setVisible(true);
		if (this.tools['toggle'])
			this.tools['toggle'].setVisible(true);
		if (this.tools['close'])
			this.tools['close'].setVisible(true);
	}

};

// EOP
;/**
 * 门户类
 * 
 * Thanks: http://www.dnexlookup.com/
 */
Ext.ux.Portal = Ext.extend(Ext.Panel, {
	layout : 'column',
	autoScroll : true,
	cls : 'x-portal',
	defaultType : 'portalcolumn',
	portlets : {},
	portViewId : null,

	// Ext2.0 example提供
	initComponent : function() {
		Ext.ux.Portal.superclass.initComponent.call(this)
		this.addEvents({
			validatedrop : true,
			beforedragover : true,
			dragover : true,
			beforedrop : true,
			drop : true,
			portletchanged : true
		})
		
	},
	// Ext2.0 example提供
	initEvents : function() {
		Ext.ux.Portal.superclass.initEvents.call(this)
		this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig)
	},
	/**
	 * 创建新的Portlet
	 */
	addPortlet : function(pInfo, pTools, portletDefinition, colAt) {
		var config = {
			autoCreate : true,
			tools : pTools,
			title : pInfo.text,
			height : pInfo.height
		}
		if (portletDefinition.hbuttons)
		    Ext.apply(config,{
			   plugins : [new Ext.ux.MaximizeTool(),new Ext.ux.plugins.HeaderButtons()],
			   hbuttons: portletDefinition.hbuttons
		    })
		else
		    Ext.apply(config,{
			   plugins : new Ext.ux.MaximizeTool()
		    })
		if (portletDefinition.xtype) {
			var xtypeConfig = {
				id : portletDefinition.id,
				xtype : portletDefinition.xtype
			}
			if (portletDefinition.config) {
				Ext.apply(xtypeConfig,portletDefinition.config)
			}
			Ext.apply(config, {
				layout : 'fit',
				items : [xtypeConfig]
			})
		} else {
			var url = portletDefinition.url
			if (url.startsWith("/"))
			    url = url + (url.indexOf('?')>0?"&":"?")+"auth="+Ext.Ajax.defaultHeaders.Authorization
			var xtypeConfig = {
				id : portletDefinition.id,
				xtype : 'iframepanel',
				defaultSrc : url
			}
			if (portletDefinition.config) {
				Ext.apply(xtypeConfig,portletDefinition.config)
			}
			Ext.apply(config, {
				layout : 'fit',
				items : [xtypeConfig]
			})
		}

		var p = new Ext.ux.Portlet(config)
		p.pInfo = pInfo
		this.portlets[pInfo.id] = p

		var col = this.items.itemAt(0) // 第1列
		if (colAt != undefined) {
			col = this.items.itemAt(colAt)
			if (col.columnWidth == 0.01) {
				col = this.items.itemAt(0)
			}
		}
		col.add(p)
		p.pClmn = col
		return p
	},
	/**
	 * 关闭指定id的Portlet
	 */
	removePortlet : function(id) {
		var p = this.portlets[id]
		if (p) {
			p.pClmn.remove(p, true)
			delete this.portlets[id]
		}
	},
	/**
	 * 关闭所有Portlet
	 */
	removeAllPortlets : function() {
		for (var i = 0; i < this.items.length; i++) {
			var c = this.items.itemAt(i)
			if (c.items)
				for (var j = c.items.length - 1; j >= 0; j--) {
					c.remove(c.items.itemAt(j), true)
				}
		}
		this.portlets = {}
	},
	/**
	 * 返回全部Portlet
	 */
	getAllPortlets : function() {
		var retVal = []
		for (var i = 0; i < this.items.length; i++) {
			var c = this.items.itemAt(i)
			for (var j = c.items.length - 1; j >= 0; j--) {
				retVal.push(c.items.itemAt(j))
			}
		}
		return retVal
	},
	/**
	 * 返回指定Id的Portlet
	 */
	getPortlet : function(id) {
		return this.portlets[id]
	},
	/**
	 * 动态改变列数（1至4）
	 */
	setColNum : function(colNum) {
		var cs = this.items
		var colWidth = 0.25
		if (colNum == 1) {
			colWidth = 0.97
		} else if (colNum == 2) {
			colWidth = 0.49
		} else if (colNum == 3) {
			colWidth = 0.33
		}

		for (var i = 0; i < colNum; i++) {
			this.items.itemAt(i).columnWidth = colWidth
		}
		for (var i = colNum; i < this.items.length; i++) {
			this.items.itemAt(i).columnWidth = 0.01
		}
	},
	// 恢复门户状态
	restorePortlets : function(colNumChanged) {
		var result, pConfig
		if (colNumChanged == undefined) {
			divo.restoreProfile(function(retValue) {
				result = retValue
			}, divo.getUserId(), this.portViewId + '-layout')
			if (result) {
				var s = new Ext.state.Provider()
				pConfig = s.decodeValue(result.msgValue)
			} // 获取布局内容
		} // 改变列数时不用记忆的布局

		var portlets = this.portalState.getVisiblePortlets()
		if (!portlets) {
			return
		}	

		this.removeAllPortlets()
		this.setColNum(this.portalState.getColNum())
		var ps = []
		for (var i = 0; i < portlets.length && i < 9; i++) {
			var p = portlets[i]
			if (p && p.id) {
				ps.push(p)
			}
		}
		ps = this.showPortlets(ps, pConfig)
		this.doLayout()
		this.portalState.restoreCollapseState(ps)
	},
	// 按指定布局显示多个Portlet
	showPortlets : function(pInfos, pConfig) {
		var ps = []
		if (pConfig) {
			for (var c = 0; c < this.columnCount; c++) {
				for (var s = 0; s < pConfig[c].length; s++) {
					var p = pConfig[c][s]
					if (!p)
						continue
					for (var i = 0; i < pInfos.length; i++) {
						if (pInfos[i].id == p.id) {
							var col = this.items.itemAt(c)
							if (col.columnWidth == 0.01)
								ps.push(this.showPortlet(pInfos[i], 0))
							else
								ps.push(this.showPortlet(pInfos[i], c))
						}
					}
				} // 某列中所有portlet循环
			} // 列循环
		} else {
			for (var i = 0; i < pInfos.length; i++) {
				ps.push(this.showPortlet(pInfos[i], 0))
			} // 调整列数后
		}

		return ps
	},
	// 显示单个Portlet
	showPortlet : function(pInfo,colAt) {
		var pd = this.getPortletDefinition(pInfo.id)
		if (!pd) return  //程序变动后，记忆的定义可能已经改变
		
		var me = this
		var pTools = [{
			id : 'close',
			handler : function(e, target, panel) {
				panel.ownerCt.remove(panel, true)
				me.fireEvent("portletchanged",panel.pInfo.id,false)
				me.portalState.save()
			}
		}]
		var h = this.portalState.getHeight(pInfo.id)
		var p = this.addPortlet({
			id : pInfo.id,
			text : pInfo.text,
			height : h || 200
		}, pTools, pd, colAt)
		
		this.fireEvent("portletchanged",pInfo.id,true)

		p.on("resize", function() {
			this.portalState.save()
		},this)

		return p
	},
	getPortletDefinition : function(id) {
		for (var i = 0; i < this.portletDefinitions.length; i++) {
			if (this.portletDefinitions[i].id == id)
				return this.portletDefinitions[i]
		}
	},
	setPortletDefinition : function(defs) {
		this.portletDefinitions = defs
	},
	setPortalState : function(ps) {
		this.portalState = ps
	},
	setPortalViewId : function(id) {
		this.portViewId = id
	}

})

Ext.reg('portal', Ext.ux.Portal)
// EOP
;/**
 * 定义 portalcolumn 布局
 */
Ext.ux.PortalColumn = Ext.extend(Ext.Container, {
	layout : 'anchor',
	autoEl : 'div',
	defaultType : 'portlet',
	cls : 'x-portal-column'
});
Ext.reg('portalcolumn', Ext.ux.PortalColumn);
;/**
 * 门户拖动支持类
 */
Ext.ux.Portal.DropZone = function(portal, cfg) {
	this.portal = portal;
	Ext.dd.ScrollManager.register(portal.body);
	Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom,
			cfg);
	portal.body.ddScrollConfig = this.ddScrollConfig;
};

Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {
	ddScrollConfig : {
		vthresh : 50,
		hthresh : -1,
		animate : true,
		increment : 200
	},

	createEvent : function(dd, e, data, col, c, pos) {
		return {
			portal : this.portal,
			panel : data.panel,
			columnIndex : col,
			column : c,
			position : pos,
			data : data,
			source : dd,
			rawEvent : e,
			status : this.dropAllowed
		};
	},

	notifyOver : function(dd, e, data) {
		var xy = e.getXY(), portal = this.portal, px = dd.proxy;

		// case column widths
		if (!this.grid) {
			this.grid = this.getGrid();
		}

		// handle case scroll where scrollbars appear during drag
		var cw = portal.body.dom.clientWidth;
		if (!this.lastCW) {
			this.lastCW = cw;
		} else if (this.lastCW != cw) {
			this.lastCW = cw;
			portal.doLayout();
			this.grid = this.getGrid();
		}

		// determine column
		var col = 0, xs = this.grid.columnX, cmatch = false;
		for (var len = xs.length; col < len; col++) {
			if (xy[0] < (xs[col].x + xs[col].w)) {
				cmatch = true;
				break;
			}
		}
		// no match, fix last index
		if (!cmatch) {
			col--;
		}

		// find insert position
		var p, match = false, pos = 0, c = portal.items.itemAt(col), items = c.items.items;

		for (var len = items.length; pos < len; pos++) {
			p = items[pos];
			var h = p.el.getHeight();
			if (h !== 0 && (p.el.getY() + (h / 2)) > xy[1]) {
				match = true;
				break;
			}
		}

		var overEvent = this.createEvent(dd, e, data, col, c, match && p
				? pos
				: c.items.getCount());

		if (portal.fireEvent('validatedrop', overEvent) !== false
				&& portal.fireEvent('beforedragover', overEvent) !== false) {

			// make sure proxy width is fluid
			px.getProxy().setWidth('auto');

			if (p) {
				px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
			} else {
				px.moveProxy(c.el.dom, null);
			}

			this.lastPos = {
				c : c,
				col : col,
				p : match && p ? pos : false
			};
			this.scrollPos = portal.body.getScroll();

			portal.fireEvent('dragover', overEvent);

			return overEvent.status;;
		} else {
			return overEvent.status;
		}

	},

	notifyOut : function() {
		delete this.grid;
	},

	notifyDrop : function(dd, e, data) {
		delete this.grid;
		if (!this.lastPos) {
			return;
		}
		var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;

		var dropEvent = this.createEvent(dd, e, data, col, c, pos !== false
				? pos
				: c.items.getCount());

		if (this.portal.fireEvent('validatedrop', dropEvent) !== false
				&& this.portal.fireEvent('beforedrop', dropEvent) !== false) {

			dd.proxy.getProxy().remove();
			dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
			if (pos !== false) {
				c.insert(pos, dd.panel);
			} else {
				c.add(dd.panel);
			}

			c.doLayout();

			this.portal.fireEvent('drop', dropEvent);

			// scroll position is lost on drop, fix it
			var st = this.scrollPos.top;
			if (st) {
				var d = this.portal.body.dom;
				setTimeout(function() {
					d.scrollTop = st;
				}, 10);
			}

		}
		delete this.lastPos;
	},

	// internal cache of body and column coords
	getGrid : function() {
		var box = this.portal.bwrap.getBox();
		box.columnX = [];
		this.portal.items.each(function(c) {
			box.columnX.push({
				x : c.el.getX(),
				w : c.el.getWidth()
			});
		});
		return box;
	}
});
;/**
 * 定义 portlet 面板类型
 * 
 * Thanks: http://extjs.com/forum/showthread.php?t=18593
 */
Ext.ux.Portlet = Ext.extend(Ext.Panel, {
	anchor : '100%',
	frame : true,
	hideMode : 'visibility',
	collapsible : true,
	draggable : true,
	cls : 'x-portlet',

	onRender : function(ct, position) {
		Ext.ux.Portlet.superclass.onRender.call(this, ct, position)

		this.resizer = new Ext.Resizable(this.el, {
			animate : true,
			duration : .6,
			easing : 'backIn',
			handles : 's',
			minHeight : this.minHeight || 100,
			pinned : false
		})
		this.resizer.on("resize", this.onResizer, this)
	},

	onResizer : function(oResizable, iWidth, iHeight, e) {
		this.setHeight(iHeight)
	},

	onCollapse : function(doAnim, animArg) {
		var o = {
			userId : this.getUserId(),
			msgCode : this.pInfo.id + '-collapsed',
			msgValue : 'Y'
		}
		divo.saveProfileAsync(o)

		this.el.setHeight("") // remove height set by resizer
		Ext.ux.Portlet.superclass.onCollapse.call(this, doAnim, animArg)
	},
	onExpand : function(doAnim, animArg) {
		var o = {
			userId : this.getUserId(),
			msgCode : this.pInfo.id + '-collapsed',
			msgValue : 'N'
		}
		divo.saveProfileAsync(o)

		Ext.ux.Portlet.superclass.onExpand.call(this, doAnim, animArg)
	},
	getConfig : function() {
		return {
			id : this.pInfo.id
		}
	}

})
Ext.reg('portlet', Ext.ux.Portlet)
//EOP
;// Thanks: http://extjs.com/forum/showthread.php?t=25042
Ext.ux.StatefulPortal = Ext.extend(Ext.ux.Portal, {
    // configurables
    columnCount:1
    // {{{
    ,initComponent:function() {

        Ext.apply(this, {}
        ); // end of apply

        // call parent
        Ext.ux.StatefulPortal.superclass.initComponent.apply(this, arguments);

    } // end of function initComponent
    // }}}
    // {{{
    ,getConfig:function() {
        var pConfig = [[]]
        
        var col;
        for(var c = 0; c < this.items.getCount(); c++) {
            col = this.items.get(c);    
            pConfig[c] = [];
            if(col.items) {
                for(var s = col.items.getCount()-1; s >= 0; s--) {
                    pConfig[c].push(col.items.items[s].getConfig());
                }
            }
        } 
        //pConfig值说明:
        //[
        // [{id:'portlet2'},{id:'portlet1'}], //第1列(打开了2个portlet,注意要倒序排列)
        // [undefined] //第2列(无portlet打开)
        //]
        return pConfig;
    }
    // }}}
    // {{{
    ,afterRender: function() {

        // call parent
        Ext.ux.StatefulPortal.superclass.afterRender.apply(this, arguments);
        this.body.setStyle('overflow-y', 'scroll');

    } // end of function afterRender
    // }}}

}); // end of extend

// register xtype
Ext.reg('statefulportal', Ext.ux.StatefulPortal);  
//EOP
;/**
 * 门户状态管理
 */
Ext.ux.PortalState = function() {
	// -------------------- private 属性 ------------------
	var s = new Ext.state.Provider()
	var sm = Ext.state.Manager
	var stateId, portal, portalId,portalViewId
	var canSave = true

	// -------------------- private 方法 ------------------
	// 状态保存
	function saveState() {
		if (!canSave) return
		
		// 记忆布局
		var config
		try {
			config = portal.getConfig()
		} catch (e) {
			config = null
		} // 最大化时出错，避免保存
		if (!config)
			return

		divo.saveProfile({
			userId : divo.getUserId(),
			msgCode : portalViewId + '-layout',
			msgValue : s.encodeValue(config)
		})

		// 记忆打开的portlet
		var portletInfos = [];
		var portlets = [];
		var colNum = portal.items.length;
		var rowIndex = 0;
		while (true) {
			var bFound = false;
			for (var i = 0; i < colNum; i++) {
				var p = portal.items.itemAt(i).items.itemAt(rowIndex);
				if (p && p.pInfo && p.pInfo.id) {
					portletInfos.push(p.pInfo)
					portlets.push(p);
					bFound = true;
				}
			}
			if (!bFound) {
				break;
			}
			rowIndex++;
		}

		if (portletInfos.length > 0)
			sm.set(stateId, portletInfos);

		// 记忆高度
		for (var i = 0; i < portlets.length; i++) {
			if (portlets[i].lastSize) {
				var h = portlets[i].lastSize.height;
				var sid = portletInfos[i].id+'-h'
				if (h)
					sm.set(sid, h);
			}
		}
	}

	// 获取保存的门户列数
	function getColNum() {
		var result
		divo.restoreProfile(function(retValue) {
			result = retValue;
		}, divo.getUserId(), portalViewId + '-column-num')

		if (result && result.msgValue)
			return parseInt(result.msgValue)
		else
			return 1
	}

	// 记忆门户列数
	function saveColNum(colNum) {
		divo.saveProfile({
			userId : divo.getUserId(),
			msgCode : portalViewId + '-column-num',
			msgValue : colNum
		})
	}

	// -------------------- public 方法 ------------------
	return {
		/**
		 * 初始化
		 */
		init : function(_portalId, _portalViewId) {
			portalId = _portalId
			stateId = _portalViewId+"-state"
			portalViewId = _portalViewId
			portal = Ext.ComponentMgr.get(portalId)
		},
		/**
		 * 保存门户状态
		 */
		save : function() {
			saveState()
		},
		/**
		 * 取得可见的门户
		 */
		getVisiblePortlets : function() {
			return sm.get(stateId);
		},
		/**
		 * 取得保存的高度
		 */
		getHeight : function(portletId) {
			return sm.get(portletId+'-h')
		},
		/**
		 * 获取保存的门户列数
		 */
		getColNum : function() {
			return getColNum()
		},
		/**
		 * 记忆门户列数
		 */
		saveColNum : function(colNum) {
			saveColNum(colNum)
		},
		/**
		 * 恢复门户的收缩状态
		 */
		restoreCollapseState : function(ps) {
			var result
			for (var i = 0; i < ps.length; i++) {
				if (ps[i]) {
					divo.restoreProfile(function(retValue) {
						result = retValue
					}, divo.getUserId(), ps[i].pInfo.id + '-collapsed')
					if (result && result.msgValue == 'Y') {
						ps[i].collapse()
					}
				}
			}
		},
		/**
		 * 可以暂时禁止保存状态
		 */
		setCanSave : function(s) {
			canSave = s
		}

	}
}
// EOP

;/**
 * 门户管理器
 */
Ext.ux.PortalManager = function(portalId,portalViewId,portletDefinitions) {
	// ----------------- 私有属性 ----------------------
	var colNumCombo, portletMenu
	var portalState, portal
	var msg

	// ----------------- 私有方法 ----------------------
	// 在portletDefinitions中添加前缀
	function adjustPortletDefinitions() {
		for (var i = 0; i < portletDefinitions.length; i++) {
			portletDefinitions[i].id = portalViewId + '-' + portletDefinitions[i].id
		}
	}

	// 创建门户管理用工具条控件
	function createToolbar() {
		var tb = []
		tb.push('->')

		portletMenu = new Ext.ux.ColumnMenu({
        	columnHeight: 100,
        	columnWidth: 148,
			id : portalId + 'menu',
			items : getPortletMenuItems()
		})
		tb.push({
			text : '内容选择...',
			iconCls : 'bmenu',
			menu : portletMenu
		})
		tb.push({
			text : '栏目数'
		})

		colNumCombo = new Ext.form.ComboBox({
			store : [[1, '1'], [2, '2'], [3, '3'], [4, '4']],
			typeAhead : true,
			width : 40,
			triggerAction : 'all',
			editable : false,
			selectOnFocus : true
		})

		tb.push(colNumCombo)
		return tb
	}

	// 生成内容菜单项
	function getPortletMenuItems() {
		var items = []
		for (var i = 0; i < portletDefinitions.length; i++) {
			items.push({
				text : portletDefinitions[i].text,
				hideOnClick:false,
				checked : false,
				checkHandler : onItemCheck
			})
		}
		return items
	}

	// 选择内容菜单项时
	function onItemCheck(item, checked) {
		var portletId = getPortletId(item.text)
		if (!checked) {
			portal.removePortlet(portletId);
			portal.doLayout();
		} else {
			var p = portal.showPortlet({
				text : item.text,
				id : portletId,
				checked : true
			});
			portal.doLayout();
			portalState.restoreCollapseState([p]);
		}
		portalState.save()
		
		onRefresh(msg)   //刚显示就要刷新
	}

	// 门户窗口关闭时
	function onPortletChanged(portletId, checked) {
		var index = getItemIndex(portletId)
		if (index && portletMenu.items.itemAt(index))
			portletMenu.items.itemAt(index).setChecked(checked, true); //第2个参数表示suppressEvent
	}

	// 栏目数改变时
	function onColNumChanged(o) {
		var colNum = o.getValue()
		if (colNum != portalState.getColNum()) {
			portalState.saveColNum(colNum)
			portal.restorePortlets(true)
		}
		
		onRefresh(msg)   //刚显示就要刷新
	}

	//通过名称获取id
	function getPortletId(text) {
		for (var i = 0; i < portletDefinitions.length; i++) {
			if (portletDefinitions[i].text == text)
				return portletDefinitions[i].id
		}
	}
	
	//通过id获得索引号
	function getItemIndex(id) {
		for (var i = 0; i < portletDefinitions.length; i++) {
			if (portletDefinitions[i].id == id)
				return [i]
		}
	}

	//刷新门户窗口内容
	function onRefresh(_msg) {
		if (!_msg) return
		
		msg = _msg
		var portlets = portal.getAllPortlets()
        for (var i = 0; i < portlets.length; i++) {
        	var p = portlets[i]
        	if (p.items) {
        		var o = p.items.items[0]
        	    if (o.showList)
        	        o.showList(msg.id,msg)
        	    else if (o.showContent)
        	        o.showContent(msg.id,msg)
        	}
        }
	}

	//驱动门户窗口做某种操作
	function onCustomAction(_msg) {
		if (!_msg) return
		
		msg = _msg
		var portlets = portal.getAllPortlets()
        for (var i = 0; i < portlets.length; i++) {
        	var p = portlets[i]
        	if (p.items) {
        		var o = p.items.items[0]
        	    if (o.doCustomAction)
        	        o.doCustomAction(msg)
        	}
        }
	}
	
	//将预定义门户视图状态变成当前用户的初始门户状态
	function setDefaultPortalState() {
		Ext.Ajax.request({
			scope : this,
			url : "/portalviews/"+portalViewId+"/"+divo.getUserId(),
			async : false,
			method : 'PUT',
			jsonData : {aa:1}
		});
	}
	
	// -------------------- public方法 -----------------------
	return {
		/**
		 * 初始化
		 */
		init : function() {
			divo.StatusBar.say("正在恢复...")
			
			portalState = new Ext.ux.PortalState()
			portalState.init(portalId,portalViewId)

			//var v = portalState.getColNum()
			//colNumCombo.setValue(v)  执行慢？
			colNumCombo.on("select",onColNumChanged,this)
			
			adjustPortletDefinitions()

			portal = Ext.ComponentMgr.get(portalId)
			portal.setPortletDefinition(portletDefinitions)
			portal.setPortalState(portalState)
			portal.setPortalViewId(portalViewId)

			portal.on('portletchanged', onPortletChanged, this)
			
			setDefaultPortalState()
			
			portalState.setCanSave(false)
			portal.restorePortlets()
			portalState.setCanSave(true)
			
			divo.StatusBar.clear()
		},
		getTbar : function() {
			return createToolbar()
		},
		onRefresh : function(msg) {
			onRefresh(msg)
		},
		onCustomAction : function(msg) {
			onCustomAction(msg)
		},
		saveState : function() {
			portalState.save()
		}
	}
}
// EOP

;/**
 * 实用例程 
 */
Ext.apply(divo.utils, {
	lowercase : function(str) {
		if (Ext.isEmpty(str)) {
			return str;
		}
		str = str.toString();
		return str.toLowerCase();
	},
	lowercaseFirstChar : function(str) {
		if (Ext.isEmpty(str)) {
			return str;
		}
		str = str.toString();
		return str.charAt(0).toLowerCase() + str.slice(1);
	},
	capitalize : function(str) {
		if (Ext.isEmpty(str)) {
			return str;
		}
		str = str.toString();
		return str.charAt(0).toUpperCase() + str.slice(1);
	},
	returnEmptyStr : function() {
		return '';
	},
	convert : {
		stringContextToBool : function(str) {
			return (str.toLowerCase() === 'true');
		}
	},
	// deep copy of an object. All references independent from object passed
	// in.
	cloneObj : function(o) {
		if (typeof(o) != 'object' || o === null) {
			return o;
		}

		var newObj = {};

		for (var i in o) {
			newObj[i] = divo.utils.cloneObj(o[i]);
		}

		return newObj;
	},

	// (Array : arr, string : child, [string seperator])
	// array to join, name of element of contained object, seperator
	// (defaults to ", ")
	joinArrayObject : function(arr, child, seperator) {
		var sOut = '';
		var sep = (seperator) ? seperator : ', ';

		for (var i = 0; i < arr.length; i++) {
			if ((arr[i])[child]) {
				sOut += (arr[i])[child] + sep;
			}
		}

		return sOut.substring(0, sOut.length - sep.length);
	},
	getLastTime : function(v) {
		if (v.length < 18)
			return "";
		var d = Date.parseDate(v, 'Y-m-d H:i:s');
		var m = d.getElapsed();
		var minutes = Math.floor(m / 1000 / 60);
		var seconds = Math.floor(m / 1000) % 60;
		var hours = Math.floor(m / 1000 / 60 / 60);
		var days = Math.floor(m / 1000 / 60 / 60 / 24);
		var time = "";
		if (days > 30)
			time = v.substring(0, 10);
		else if (days > 0)
			time = days + '天前';
		else if (hours > 0)
			time = hours + '小时前';
		else if (minutes > 0)
			time = minutes + '分钟前';
		else
			time = seconds + '秒前';
		return time;
	}

});
// EOP
;/**
 * 按需加载JS文件
 * Thanks: http://www.qudee.com/as_bb51d59a62b8ef9ce5cf54053bee94f2.html
 */
divo.utils.JsLoader = function() {

	this.load = function(url,fireOnSuccess) {
		// 获取所有的<script>标记
		var ss = document.getElementsByTagName("script");
		// 判断指定的文件是否已经包含，如果已包含则触发onsuccess事件并返回
		for (i = 0;i < ss.length; i++) {
			if (ss[i].src && ss[i].src.indexOf(url) != -1) {
				if (fireOnSuccess)
  				    //this.onSuccess();
				    setTimeout(this.onSuccess,500)
				return;
			}
		}
		// 创建script结点,并将其属性设为外联JavaScript文件
		s = document.createElement("script");
		s.type = "text/javascript";
		s.src = url;
		// 获取head结点，并将<script>插入到其中
		var head = document.getElementsByTagName("head")[0];
		head.appendChild(s);

		// 获取自身的引用
		var self = this;
		// 对于IE浏览器，使用readystatechange事件判断是否载入成功
		// 对于其他浏览器，使用onload事件判断载入是否成功
		// s.onload=s.onreadystatechange=function(){
		s.onload = s.onreadystatechange = function() {
			// 在此函数中this指针指的是s结点对象，而不是JsLoader实例,
			// 所以必须用self来调用onsuccess事件，下同。
			if (this.readyState && this.readyState == "loading")
				return;
		    if (fireOnSuccess)
			    setTimeout(self.onSuccess,500); //js加载后执行函数体需要时间
		}
		s.onerror = function() {
			head.removeChild(s);
			self.onFailure(url);
		}
	};
	// 定义载入成功事件
	this.onSuccess = function() {
	};
	// 定义失败事件
	this.onFailure = function(url) {
         alert(url+"载入失败！");
	};
}
// EOP
;/**
 * Base64 encode / decode http://www.webtoolkit.info/
 */
divo.utils.base64 = function() {
	// private property
	var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

	// private method for UTF-8 encoding
	var _utf8_encode = function(string) {
		string = string.replace(/\r\n/g, "\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {
			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			} else if ((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			} else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
		}

		return utftext;
	};

	// private method for UTF-8 decoding
	var _utf8_decode = function(utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while (i < utftext.length) {
			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			} else if ((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i + 1);
				string += String.fromCharCode(((c & 31) << 6)
						| (c2 & 63));
				i += 2;
			} else {
				c2 = utftext.charCodeAt(i + 1);
				c3 = utftext.charCodeAt(i + 2);
				string += String.fromCharCode(((c & 15) << 12)
						| ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
		}

		return string;
	};

	return {
		// public method for encoding
		encode : function(input) {
			var output = "";
			var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
			var i = 0;

			input = _utf8_encode(input);

			while (i < input.length) {
				chr1 = input.charCodeAt(i++);
				chr2 = input.charCodeAt(i++);
				chr3 = input.charCodeAt(i++);

				enc1 = chr1 >> 2;
				enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
				enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
				enc4 = chr3 & 63;

				if (isNaN(chr2)) {
					enc3 = enc4 = 64;
				} else if (isNaN(chr3)) {
					enc4 = 64;
				}

				output = output + _keyStr.charAt(enc1)
						+ _keyStr.charAt(enc2) + _keyStr.charAt(enc3)
						+ _keyStr.charAt(enc4);
			}

			return output;
		},

		// public method for decoding
		decode : function(input) {
			var output = "";
			var chr1, chr2, chr3;
			var enc1, enc2, enc3, enc4;
			var i = 0;

			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

			while (i < input.length) {
				enc1 = _keyStr.indexOf(input.charAt(i++));
				enc2 = _keyStr.indexOf(input.charAt(i++));
				enc3 = _keyStr.indexOf(input.charAt(i++));
				enc4 = _keyStr.indexOf(input.charAt(i++));

				chr1 = (enc1 << 2) | (enc2 >> 4);
				chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
				chr3 = ((enc3 & 3) << 6) | enc4;

				output = output + String.fromCharCode(chr1);

				if (enc3 != 64) {
					output = output + String.fromCharCode(chr2);
				}
				if (enc4 != 64) {
					output = output + String.fromCharCode(chr3);
				}
			}

			output = _utf8_decode(output);

			return output;
		}
	}
}()
//EOP

;/**
 * 检测并提示大写状态 
 * Thanks: http://17thdegree.com/wp/capslock.html
 */
divo.utils.CapsLock = function() {
	return {
		init : function() {
			var id = Ext.id();
			this.alertBox = Ext.DomHelper.append(document.body, {
				tag : 'div',
				style : 'width:10em',
				children : [{
					tag : 'div',
					style : 'text-align: center; background-color: black;color: white;',
					html : '现在是字母大写状态',
					id : id
				}]
			}, true);
			Ext.fly(id).boxWrap();
			this.alertBox.hide();

			var pwds = Ext.query("INPUT[type='password']");
			for (var i = 0;i < pwds.length; i++) {
				Ext.get(pwds[i].id)
						.on(
								'keypress',
								this.keypress.createDelegate(this, pwds[i],
										true), this);
			}
		},
		keypress : function(e, el) {
			var charCode = e.getCharCode();
			if ((e.shiftKey && charCode >= 97 && charCode <= 122)
					|| (!e.shiftKey && charCode >= 65 && charCode <= 90)) {
				this.showWarning(el);
			} else {
				this.hideWarning();
			}
		},
		showWarning : function(el) {
			var x = Ext.fly(el).getX();
			var width = Ext.fly(el).getWidth();
			var y = Ext.fly(el).getY();

			this.alertBox.setXY([x + width + 100, y]);
			this.alertBox.show();
		},
		hideWarning : function() {
			this.alertBox.hide();
		}
	}
	
}()
//EOP
	;/* --------- 扩展 JavaScript String 类 ------------- */
String.prototype.ltrim = function() {
	return this.replace(/(^\s*)/g, "");
}
String.prototype.rtrim = function() {
	return this.replace(/(\s*$)/g, "");
}
String.prototype.trim = function() {
	var re = /^\s+|\s+$/g;
	return function() {
		return this.replace(re, "");
	};
}();

/**
 * String.pad(length: Integer, [substring: String = " "], [type: Integer = 0]):
 * String type: 0 = left, 1 = right and 2 = both sides
 */
String.prototype.pad = function(l, s, t) {
	return s || (s = " "), (l -= this.length) > 0 ? (s = new Array(Math.ceil(l
			/ s.length)
			+ 1).join(s)).substr(0, t = !t ? l : t == 1 ? 0 : Math.ceil(l / 2))
			+ this + s.substr(0, l - t) : this;
} // Thanks: http://snippets.dzone.com/posts/show/900

String.prototype.startsWith = function(s) {
	return this.indexOf(s) == 0;
}
String.prototype.endsWith = function(str) {
	return (this.match(str + "$") == str)
}
//Thanks: http://www.moon-soft.com/doc/38917.htm
String.prototype.getHzLength = function(){
	var arr = this.match(/[^\x00-\xff]/ig)
	return this.length+(arr==null?0:arr.length)
}
//Thanks: http://topic.csdn.net/t/20020419/14/660320.html
String.prototype.brkLine = function(charlen) {

	s = ""
	l = 0;
	p = 0;
	for (i = 0; i < this.length; i++) {
		a = this.charAt(i);
		s += a;
		if (p != 0) {
			if (a == ">")
				p = 0;
		} else {
			if (a != "<") {

				if (!/^[\x00-\xff]/.test(a))
					l++;
				l++;

				if (l >= charlen) {
					s += "<br />";
					l = 0;
				}
			} else if (this.substr(i, 4).toLowerCase() == "<br />") {
				s += "br>";
				i += 4;
				l = 0;
			} else
				p = 1;

		}
	}
	return s;
}
//EOP
;/* --------- 扩展 JavaScript Array 类 ------------- */
//Array.prototype.contains = function(element) {
//    for (var i = 0; i < this.length; i++) {
//            if (this[i] == element) {
//                return true;
//            }
//    }        
//	return false;
//};
Array.prototype.contains = function(element) {
	return this.indexOf(element) !== -1;
};

//EOP
; /* global Ext */

 /*
    ext-basex 3.0
  ************************************************************************************

    Ext.lib.Ajax enhancements:
     - adds EventManager Support to Ext.lib.Ajax (if Ext.util.Observable is present in the stack)
     - adds Synchronous Ajax Support ( options.async =false )
     - Permits IE7 to Access Local File Systems using IE's older ActiveX interface
       via the forceActiveX property
     - Pluggable Form encoder (encodeURIComponent is still the default encoder)
     - Corrects the Content-Type Headers for posting JSON (application/json)
       and XML (text/xml) data payloads and sets only one value (per RFC)
     - Adds fullStatus:{ isLocal, proxied, isOK, isError, isTimeout, isAbort, error, status, statusText}
       object to the existing Response Object.
     - Adds standard HTTP Auth support to every request (XHR userId, password config options)
     - options.method prevails over any method derived by the lib.Ajax stack (DELETE, PUT, HEAD etc).
     - Adds named-Priority-Queuing for Ajax Requests
     - adds Script=Tag support for foreign-domains (proxied:true) with configurable callbacks.

  ************************************************************************************
  * Author: Doug Hendricks. doug[always-At]theactivegroup.com
  * Copyright 2007-2008, Active Group, Inc.  All rights reserved.
  ************************************************************************************

  License: ext-basex is licensed under the terms of : GNU Open Source GPL 3.0 license:

  Commercial use is prohibited without contacting licensing[at]theactivegroup.com.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see < http://www.gnu.org/licenses/gpl.html>.

  * Donations are welcomed: http://donate.theactivegroup.com

  */


(function(){
var A = Ext.lib.Ajax;


A.Queue = function(config){

     config = config ? (config.name? config : {name:config}) :{} ;
     Ext.apply(this,config,
     {
        name     : 'q-default'
       ,priority : 5
       ,FIFO     : true //false implies LIFO
       ,callback : null //optional callback when queue is emptied
       ,scope    : null //scope of callback
       ,suspended: false
       ,progressive : false //if true, one queue item is dispatched per poll interval
      });
      this.requests = [];
      this.pending = false;
      //assert/resolve to 0-9
      this.priority = this.priority>9 ? 9 : (this.priority<0?0:this.priority );

};

Ext.extend(A.Queue, Object ,{

     add      : function(req){

        var permit = A.events? A.fireEvent('beforequeue', this, req) : true;
        if(permit !== false){
            this.requests.push(req);
            this.pending = true;
            A.pendingRequests++;
            if(this.manager){
               this.manager.start();
            }
        }
     }
    ,suspended : false
    ,activeRequest : null
    ,next     : function(){
        var req = this.requests[this.FIFO?'shift':'pop']();
        this.pending = !!this.requests.length;
        return req;
        }

    ,clear    : function(){
        this.requests.length = 0;
        this.pending = false;
        if(A.events){A.fireEvent('queueempty', this)};
        }
     // suspend/resume from dispatch control
    ,suspend  : function(){this.suspended = true;}
    ,resume   : function(){this.suspended = false;}
    ,requestNext : function(){
       var req;
       this.activeRequest = null;
       if(!this.suspended && (req = this.next())){
           A.pendingRequests--;
           this.activeRequest = req.active ? A.request.apply(A,req) : null;
           if(this.requests.length==0){

               //queue emptied callback
               if(this.callback)
                 {this.callback.call(this.scope||null,this);}
               this.clear();
           }

       }
       return this.activeRequest;
    }
 });


A.QueueManager = function(config){

    Ext.apply(this, config||{},
    {  quantas      : 10, //adjustable milliseconds deferred dispatch value
      priorityQueues: [[],[],[],[],[],[],[],[],[],[]], //iterable array (0-9) of prioritized queues:
       queues       :{}
    });
};


Ext.extend(A.QueueManager, Object,{

     getQueue     : function(name){ return this.queues[name]; }

    ,createQueue  : function(config){
        if(!config){return null;}

        var q = new A.Queue(config);
        q.manager = this;
        this.queues[q.name] = q;

        var pqa = this.priorityQueues[q.priority];
        if(pqa && pqa.indexOf(q.name)==-1){pqa.push(q.name);}

        return q;
     }
     //Remove a Queue by passed name or Queue Object reference
    ,removeQueue  : function(q){
        if(q && (q = this.getQueue(q.name||q) )){
            q.suspend();
            q.clear();  //purge any pending requests
            this.priorityQueues[q.priority].remove(q);
            delete this.queues[q.name];
        }
    }
    ,start   :function(){
        if(!this.started){
         this.started = true;
         this.dispatch();
        }
    }

    ,suspendAll : function(){
        forEach(this.queues, function(Q){ Q.suspend(); });
    }
    ,resumeAll : function(){
        forEach(this.queues, function(Q){ Q.resume(); });
        this.start();
    }


   /* Default Dispatch mode: progressive
    * false to exhaust a priority queue until empty during dispatch (sequential)
    * true to dispatch a single request from each priority queue until all queues exhausted.
    * This option may be set on the Queue itself as well.
    */
    ,progressive : false

    ,stop    :function(){ this.started = false; }

    /* main Request dispatch loop.  This keeps the maximum allowed number of requests
     * going at any one time (based on defined queue priority and dispatch mode (see progressive).
     */
    ,dispatch   : function(){
        var qm = this, qmq = qm.queues;
        var disp = function(qName){
             var q = qmq[qName];
             if(q && !q.suspended){
                 while(q.pending && !q.suspended && A.pendingRequests && A.activeRequests < A.maxConcurrentRequests){
                    q.requestNext();
                    if(q.progressive || qm.progressive){ break;} //progressive, take the first one off each queue only
                 }
                 //keep going?
                 return !!A.pendingRequests ? (A.activeRequests < A.maxConcurrentRequests) :false;
             }
        };

        Ext.each(this.priorityQueues ,function(queues){
            //queues == array of queue names
            if(!A.pendingRequests ){return false;}
            return Ext.each(queues||[],disp,this) === undefined?true:false;
        }, this);

        !!A.pendingRequests ? this.dispatch.defer(this.quantas,this): this.stop();

     }
});



Ext.apply( A ,
{

  queueManager      : new A.QueueManager(),

  //If true (or queue config object) ALL requests are queued
  queueAll          : false,

  //the Current number of active Ajax requests.
  activeRequests    : 0,

  //the Current number of pending Queued requests.
  pendingRequests   : 0,

  //Specify the maximum allowed during concurrent Queued browser (XHR) requests
  maxConcurrentRequests : 10,

  /* set True as needed, to coerce IE to use older ActiveX interface */
  forceActiveX      :false,

  /* Global default may be toggled at any time */
  async       :true,

  createXhrObject:function(transactionId)
        {
            var obj={  status:{isError:false}
                     , tId:transactionId}, http;
            try
            {
              if(Ext.isIE7 && !!this.forceActiveX){throw("IE7forceActiveX");}
              obj.conn= new XMLHttpRequest();
            }
            catch(eo)
            {
                for (var i = 0; i < this.activeX.length; ++i) {
                    try
                    {
                        obj.conn= new ActiveXObject(this.activeX[i]);

                        break;
                    }
                    catch(e) {
                    }
                }
            }
            finally
            {
                obj.status.isError = typeof(obj.conn) == 'undefined';
            }
            return obj;

        }

        /* Replaceable Form encoder */
    ,encoder : encodeURIComponent

    ,serializeForm : function(form) {
                if(typeof form == 'string') {
                    form = (document.getElementById(form) || document.forms[form]);
                }

                var el, name, val, disabled, data = '', hasSubmit = false;
                for (var i = 0; i < form.elements.length; i++) {
                    el = form.elements[i];
                    disabled = form.elements[i].disabled;
                    name = form.elements[i].name;
                    val = form.elements[i].value;

                    if (!disabled && name){
                        switch (el.type)
                                {
                            case 'select-one':
                            case 'select-multiple':
                                for (var j = 0; j < el.options.length; j++) {
                                    if (el.options[j].selected) {
                                        if (Ext.isIE) {
                                            data += this.encoder(name) + '=' + this.encoder(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
                                        }
                                        else {
                                            data += this.encoder(name) + '=' + this.encoder(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
                                        }
                                    }
                                }
                                break;
                            case 'radio':
                            case 'checkbox':
                                if (el.checked) {
                                    data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                }
                                break;
                            case 'file':

                            case undefined:

                            case 'reset':

                            case 'button':

                                break;
                            case 'submit':
                                if(hasSubmit === false) {
                                    data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                    hasSubmit = true;
                                }
                                break;
                            default:
                                data += this.encoder(name) + '=' + this.encoder(val) + '&';
                                break;
                        }
                    }
                }
                data = data.substr(0, data.length - 1);
                return data;
    }
    ,getHttpStatus: function(reqObj){

            var statObj = {
                     status:0
                    ,statusText :''
                    ,isError    :false
                    ,isLocal    :false
                    ,isOK       :false
                    ,error      :null
                    ,isAbort    :false
                    ,isTimeout  :false};

            try {
                if(!reqObj){throw('noobj');}
                statObj.status = reqObj.status;
                statObj.readyState = reqObj.readyState;
                statObj.isLocal = (!reqObj.status && location.protocol == "file:") ||
                          ( Ext.isSafari && reqObj.status === undefined);

                statObj.isOK = (
                    statObj.isLocal ||
                      (statObj.status == 304 || statObj.status == 1223 ||
                      (statObj.status > 199 && statObj.status < 300) )
                    );

                statObj.statusText = reqObj.statusText || '';
               } catch(e){} //status may not avail/valid yet (or called too early).

            return statObj;

     }
    ,handleTransactionResponse:function(o, callback, isAbort){

        callback = callback || {};
        var responseObject=null;
        this.activeRequests--;

        if(!o.status.isError){
            o.status = this.getHttpStatus(o.conn);
            /* create and enhance the response with proper status and XMLDOM if necessary */
            responseObject = this.createResponseObject(o, callback.argument, isAbort);
        }

        if(o.status.isError){
         /* checked again in case exception was raised - ActiveX was disabled during XML-DOM creation?
          * And mixin everything the XHR object had to offer as well
          */
           responseObject = Ext.apply({},responseObject||{},this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false)));

        }

        responseObject.options = o.options;
        responseObject.fullStatus = o.status;

        if(!this.events || this.fireEvent('status:'+o.status.status ,o.status.status, o, responseObject, callback, isAbort) !== false){

             if (o.status.isOK && !o.status.isError) {
                if(!this.events || this.fireEvent('response',o, responseObject, callback, isAbort) !== false){
                    if (callback.success) {
                        callback.success.call(callback.scope||null,responseObject);
                    }
                }
             } else {
                  if(!this.events || this.fireEvent('exception',o ,responseObject, callback, isAbort) !== false){
                    if (callback.failure) {
                        callback.failure.call(callback.scope||null,responseObject);
                    }
                  }
             }
        }

        if(o.options.async){
            this.releaseObject(o);
            responseObject = null;
        }else{
            this.releaseObject(o);
            return responseObject;
        }

    },

    createResponseObject:function(o, callbackArg, isAbort){
        var obj = {responseXML   :null,
                   responseText  :'',
                   responseStream : null,
                   getResponseHeader : {},
                   getAllResponseHeaders : ''
                   };

        var headerObj = {},headerStr='';

        if(isAbort !== true){
            try{  //to catch bad encoding problems here
                obj.responseText = o.conn.responseText;
                obj.responseStream = o.conn.responseStream||null;
            }catch(e){
                o.status.isError = true;
                o.status.error = e;
            }

            try{
                obj.responseXML = o.conn.responseXML || null;
            } catch(ex){}

            try{
                headerStr = o.conn.getAllResponseHeaders()||'';
            } catch(ex1){}

            if((o.status.isLocal || o.proxied) && typeof obj.responseText == 'string' ){

               o.status.isOK = !o.status.isError && ((o.status.status = (!!obj.responseText.length)?200:404) == 200);

               if(o.status.isOK && (!obj.responseXML || (obj.responseXML && obj.responseXML.childNodes.length === 0))){

                    var xdoc=null;
                    try{   //ActiveX may be disabled
                        if(window.ActiveXObject){
                            xdoc=new ActiveXObject("MSXML2.DOMDocument.3.0");
                            xdoc.async=false;

                            xdoc.loadXML(obj.responseText);
                        }else{
                            var domParser=null;
                            try{  //Opera 9 will fail parsing non-XML content, so trap here.
                                domParser = new DOMParser();
                                xdoc = domParser.parseFromString(obj.responseText, 'application\/xml');
                            }catch(exP){}
                            finally{domParser = null;}
                        }
                    } catch(exd){
                        o.status.isError = true;
                        o.status.error = exd;
                    }
                    obj.responseXML = xdoc;
                }
                if(obj.responseXML){
                    var parseBad =  (obj.responseXML.documentElement && obj.responseXML.documentElement.nodeName == 'parsererror') ||
                                (obj.responseXML.parseError || 0) !== 0 ||
                                obj.responseXML.childNodes.length === 0;
                    if(!parseBad){
                        headerStr = 'Content-Type: ' + (obj.responseXML.contentType || 'text\/xml') + '\n' + headerStr ;
                    }
                }
            }
        } //isAbort?

        var header = headerStr.split('\n');
        for (var i = 0; i < header.length; i++) {
            var delimitPos = header[i].indexOf(':');
            if (delimitPos != -1) {
                headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
            }
        }
        o.status.proxied    = !!o.proxied;

        Ext.apply(obj,{
           tId                  : o.tId,
           status               : o.status.status,
           statusText           : o.status.statusText,
           getResponseHeader    : headerObj,
           getAllResponseHeaders: headerStr,
           fullStatus           : o.status
        });

        if (typeof callbackArg != 'undefined') {
            obj.argument = callbackArg;
        }

        return obj;
    },
    setDefaultPostHeader:function(contentType){
        this.defaultPostHeader = contentType;
    },

    setDefaultXhrHeader:function(bool){
        this.useDefaultXhrHeader = bool||false;
    },

    request : function(method, uri, cb, data, options) {

        options = Ext.apply({
               async    :this.async || false,
               headers  :false,
               userId   :null,
               password :null,
               xmlData  :null,
               jsonData :null,
               queue    :null,
               proxied  :false
               }, options||{});

        if(!this.events || this.fireEvent('request', method, uri, cb, data, options) !== false){

               //Named priority queues
               if((options.queue || (options.queue = this.queueAll||null))  && !options.queued){
                   var q = options.queue, qname = q.name || 'default', qm=this.queueManager;
                   q = qm.getQueue(qname) || qm.createQueue(q);
                   options.queue  = q;
                   options.queued = true;

                   var req= [method, uri, cb, data, options];
                   req.active=true;
                   q.add(req);

                   return { tId:this.transactionId++
                        ,queued: true
                        ,request:req
                        ,options:options
                        };
               }

               var hs = options.headers;
               if(hs){
                    for(var h in hs){
                        if(hs.hasOwnProperty(h)){
                            this.initHeader(h, hs[h], false);
                        }
                    }
               }
               var cType = this.headers['Content-Type']||null;
               //remove to ensure only ONE is passed later.(per RFC)
               delete this.headers['Content-Type'];
               if(options.xmlData){
                    cType || (cType = 'text/xml');
                    method = 'POST';
                    data = options.xmlData;
               } else if(options.jsonData){
                    cType || (cType = 'application/json');
                    method = 'POST';
                    data = typeof options.jsonData == 'object' ? Ext.encode(options.jsonData) : options.jsonData;
               }
               if(data){
                    cType || (cType = this.useDefaultHeader?this.defaultPostHeader:null );
                    if(cType){this.initHeader('Content-Type', cType , false );}
               }

                //options.method prevails over any derived method.
               return this.makeRequest(options.method || method, uri, cb, data, options);
        }
        return null;

    }
    ,getConnectionObject:function(uri, options)
        {
            var o,f,e=Ext.emptyFn;
            var tId = this.transactionId;

            try
            {
              if(f = options.proxied){ /*scriptTag Support */

                o = { tId: tId,
                      status  : {},
                      proxied : true,
                      conn: {
                          el            : null,
                          send          : function(){
                                var doc= (f.target||window).document, head = doc.getElementsByTagName("head")[0];
                                if(head && this.el){head.appendChild(this.el);}
                          },
                          abort             : function(){this.readyState = 0;},
                          setRequestHeader  : e,
                          getAllResponseHeaders: e,
                          onreadystatechange: null,
                          readyState        : 0,
                          status            : 0,
                          responseText      : null,
                          responseXML       : null
                       },
                       debug   : f.debug,
                       cbName  : f.callbackName  || 'basexCallback' + tId,
                       cbParam : f.callbackParam || null
                    };

                window[o.cbName] = o.cb = function(content,request){
                     this.responseText = !!content?content:null;

                     this.readyState = 4;
                     this.status     = !!content?200:404;

                     if(typeof this.onreadystatechange == 'function')
                        {this.onreadystatechange();}

                     if(!request.debug){
                         Ext.removeNode(this.el);
                         this.el = null;
                     }

                     window[request.cbName] = undefined;
                     try{delete window[request.cbName];}catch(ex){}


                }.createDelegate(o.conn,[o],true);

                o.conn.open = function(){
                   this.el= domNode(f.tag || 'script'
                                     ,{type :"text/javascript"
                                      , src :o.cbParam?uri + (uri.indexOf("?") != -1 ? "&" : "?") + String.format("{0}={1}", o.cbParam , o.cbName):uri
                                      }
                                     , null
                                     , f.target
                                     , true);

                   };

                o.conn.readyState = 1; //show CallInProgress
                if(typeof o.conn.onreadystatechange == 'function')
                    {o.conn.onreadystatechange();}

                options.async = true;  //force timeout support

              }else{
                o = this.createXhrObject(tId);
              }
              if (o) {
                   this.transactionId++;
              }
            }
            catch(ex3) {
            }
            finally
            {
                return o;
            }
    }
    ,makeRequest:function(method, uri, callback, postData, options){

        var o = this.getConnectionObject(uri ,options);

        if (!o || o.status.isError) {
                return Ext.apply(o,this.handleTransactionResponse(o, callback));
        } else {

                o.options = options;
                try{
                    o.conn.open(method.toUpperCase(), uri, options.async, options.userId, options.password);
                    o.conn.onreadystatechange=this.onReadyState ?
                           this.onReadyState.createDelegate(this,[o],0):Ext.emptyFn;
                } catch(ex){
                    o.status.isError = true;
                    o.status.error = ex;
                    return Ext.apply(o,this.handleTransactionResponse(o, callback));
                }
                this.activeRequests++;
                if (this.useDefaultXhrHeader) {
                    if (!this.defaultHeaders['X-Requested-With']) {
                    this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
                    }
                }

                if (this.hasDefaultHeaders || this.hasHeaders) {
                    this.setHeader(o);
                }

                if(o.options.async){ //Timers for syncro calls won't work here, as it's a blocking call
                    this.handleReadyState(o, callback);
                }

                try{
                  if(!this.events || this.fireEvent('beforesend', o, method, uri, callback, postData, options) !== false){
                       o.conn.send(postData || null);
                  }
                } catch(exr){
                    o.status.isError = true;
                    o.status.error = exr;

                    return Ext.apply(o,this.handleTransactionResponse(o, callback));
                    }

                return options.async?o:Ext.apply(o,this.handleTransactionResponse(o, callback));
            }
    }

   ,abort:function(o, callback, isTimeout){

            if(o && o.queued && o.request){
                o.request.active = o.queued = false;
                if(this.events){
                    this.fireEvent('abort', o, callback);
                }
            }
            else if (o && this.isCallInProgress(o)) {
                o.conn.abort();
                window.clearInterval(this.poll[o.tId]);
                delete this.poll[o.tId];
                if (isTimeout) {
                    delete this.timeout[o.tId];
                }

                o.status.isAbort = !(o.status.isTimeout = isTimeout||false);
                if(this.events){
                    this.fireEvent(isTimeout?'timeout':'abort', o, callback);
                }

                this.handleTransactionResponse(o, callback, true);

                return true;
            }
            else {
                return false;
            }
    }

    ,clearAuthenticationCache:function(url) {

       try{

         if (Ext.isIE) {
           // IE clear HTTP Authentication, (but ALL realms though)
           document.execCommand("ClearAuthenticationCache");
         }
         else {
           // create an xmlhttp object
           var xmlhttp;
           if( xmlhttp = new XMLHttpRequest()){
               // prepare invalid credentials
               xmlhttp.open("GET", url || '/@@' , true, "logout", "logout");
               // send the request to the server
               xmlhttp.send("");
               // abort the request
               xmlhttp.abort.defer(100,xmlhttp);
           }
         }
       } catch(e) {        // There was an error

       }
     }

});
 /* private --
 <script and link> tag support
 */
 var domNode = function(tag, attributes, callback, context, deferred){
        attributes = Ext.apply({},attributes||{});
        context || (context = window);

        var node = null, doc= context.document, head = doc.getElementsByTagName("head")[0];

        if(doc && head && (node = doc.createElement(tag))){
            for(var attrib in attributes){
                  if(attributes.hasOwnProperty(attrib) && attrib in node){
                      node.setAttribute(attrib, attributes[attrib]);
                  }
            }

            if(callback){
                var cb = (callback.success||callback).createDelegate(callback.scope||null,[callback],0);
                if(Ext.isIE){
                     node.onreadystatechange = function(){
                          if(/loaded|complete|4/i.test(String(this.readyState))){
                                 cb();
                          }
                      }.createDelegate(node);
                }else if(Ext.isSafari3 && tag == 'script'){
                    //has DOM2 support
                    node.addEventListener("load", cb );
                }else if(Ext.isSafari) {
                    cb.defer(50);
                }else{
                    /* Gecko/Safari has no event support for link tag
                       so just defer the callback 50ms (optimistic)
                    */
                    tag !== 'link' || Ext.isOpera ? Ext.get(node).on('load',cb) : cb.defer(50);
                }

            }
            if(!deferred){head.appendChild(node);}
       }
       return node;

    };

if(Ext.util.Observable){

  Ext.apply( A ,{

   events:{request     :true,
           beforesend  :true,
           response    :true,
           exception   :true,
           abort       :true,
           timeout     :true,
      readystatechange :true,
           beforequeue :true,
           queue       :true,
           queueempty  :true
    }

   /*
     onStatus
     define eventListeners for a single (or array) of HTTP status codes.
   */
   ,onStatus:function(status,fn,scope,options){
        var args = Array.prototype.slice.call(arguments, 1);
        status = [].concat(status||[]);
        Ext.each(status,function(statusCode){
            statusCode = parseInt(statusCode,10);
            if(!isNaN(statusCode)){
                var ev = 'status:'+statusCode;
                this.events[ev] || (this.events[ev] = true);
                this.on.apply(this,[ev].concat(args));
            }
        },this);
   }
   /*
        unStatus
        unSet eventListeners for a single (or array) of HTTP status codes.
   */
   ,unStatus:function(status,fn,scope,options){
           var args = Array.prototype.slice.call(arguments, 1);
           status = [].concat(status||[]);
           Ext.each(status,function(statusCode){
                statusCode = parseInt(statusCode,10);
                if(!isNaN(statusCode)){
                    var ev = 'status:'+statusCode;
                    this.un.apply(this,[ev].concat(args));
                }
           },this);
      }
    ,onReadyState : function(){
         this.fireEvent.apply(this,['readystatechange'].concat(Array.prototype.slice.call(arguments, 0)));
    }

  }, new Ext.util.Observable());

  Ext.hasBasex = true;
}

})();

//Array, object iteration support
(function(){
    Ext.stopIteration = {stopIter:true};

    /* Fix for Opera, which does not seem to include the map function on Array's */

    Ext.applyIf( Array.prototype,{
       map : function(fun,scope){
        var len = this.length;
        if(typeof fun != "function"){
            throw new TypeError();
        }
        var res = new Array(len);

        for(var i = 0; i < len; i++){
            if(i in this){
                res[i] = fun.call(scope||this, this[i], i, this);
            }
        }
        return res;
      }
       /*
       Array forEach Iteration
         based on previous work by: Dean Edwards (http://dean.edwards.name/weblog/2006/07/enum/)
         Gecko already supports forEach for Arrays : see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:forEach
       */
      ,forEach : function(block, scope) {

        if(typeof block  != "function"){
            throw new TypeError();
        }
        var i=0,length = this.length;
        while(i < length ){ block.call(scope||null, this[i], i++, this ); }
       },

       include: function(value) {               //Boolean: is value present in Array
           //use native indexOf if available
           if(typeof this.indexOf == 'function'){ return this.indexOf(value) != -1; }
           var found = false;
           try{
             this.forEach(function(item,index) { if(found = (item == value))throw Ext.stopIteration; });
           }catch(exc){if(exc != Ext.stopIteration){ throw exc;} }
           return found;
       },
       // Using iterFn, traverse the array, push the current element value onto the
       // result if the iterFn returns true
       filter: function(iterFn, scope) {
           var a = [];
           this.forEach( function(value, index) {
             if (iterFn.call(scope, value, index)){ a.push(value);}
           });
           return a;
       },

       compact: function(deep){                     //Remove null, undefined array elements
          var a=[];
          this.forEach(function(v){
              (v ===null || v=== undefined) || a.push(deep && Ext.isArray(v)?v.compact(): v );
          },this);
          return a;
       },

       flatten : function(){                    //flatten: [1,2,3,[4,5,6]]  -> [1,2,3,4,5,6]
         var a=[];
         this.forEach(function(v){
               Ext.isArray(v) ? (a = a.concat(v)) : a.push(v);
          },this);
         return a;
       },
       unique: function(sorted /* sort optimization */) {  //unique: [1,3,3,4,4,5]  -> [1,3,4,5]
           var a = [];
           this.forEach(function(value, index){
             if (0 == index || (sorted ? a.last() != value : !a.include(value))){a.push(value);}
           },this);
           return a;
       },
       // search array values based on regExpression pattern returning
       // test (and optionally execute function(value,index) on test before returned)
       grep: function(pattern, iterFn, scope) {
           var a = [];
           iterFn || (iterFn = function(value){ return value; });
           var fn = scope? iterFn.createDelegate(scope): iterFn ;

           if (typeof pattern == 'string'){ pattern = new RegExp(pattern); }
           this.forEach(function(value, index) {
             if(pattern.test(value)){ a.push( fn(value, index)); }
           });
           return a;
       },
       first: function() { return this[0]; },

       last: function() { return this[this.length - 1]; },

       clone: function() { return [].concat(this); },

       clear: function() { this.length = 0; }

    });

       // enumerate custom class properties (not prototypes)
       // usually only called by the global forEach function
    Ext.applyIf(Function.prototype,{
       forEach : function( object, block, context) {
        if(typeof block != "function"){
            throw new TypeError();
        }
        for (var key in object) {
               if (typeof this.prototype[key] == "undefined") {  //target instance properties
                  try{block.call(context || null, object[key], key, object);}catch(e){}
               }
        }
      }
    });

       // character enumeration
    Ext.applyIf(String.prototype,{
       forEach : function(block, context) {
            if(typeof block != "function"){
                throw new TypeError();
            }
            var str = this.toString();
            context = context || this;
            var ar = str.split("")||[];
            ar.forEach( function(chr, index) {
                try{block.apply(context,[ chr, index, str]);}catch(e){}
            },ar);
       },
       trim : function(){
           var re = /^\s+|\s+$/g;
           return function(){ return this.replace(re, ""); };
         }()

    });

       // globally resolve forEach enumeration
    window.forEach = function(object, block, context) {
        context = context || object;
        if (object) {
            if(typeof block != "function"){
                throw new TypeError();
            }
            var resolve = Object; // default
            if (object instanceof Function) {
                // functions have a "length" property
                resolve = Function;
            } else if (object.forEach instanceof Function) {
                // the object implements a custom forEach method so use that
                object.forEach(block, context);
                return;
            }
            return resolve.forEach(object, block, context);
        }
     };
})();
;/**
 * 定义命名空间 
 */
Ext.namespace("divo.home");
//EOP

;/**
 * 登录
 * 
 * --------------------------------------------------------------
 * 消息：
 * 登录成功，显示主页
 * 
 * 消息名：     			
 * j.showHomepage   
 * 
 * 消息内容：
 * 无
 * --------------------------------------------------------------
*/
divo.home.Login = function() {
	return {
		loginUrl : '/login',
		loginFormConfig : {
			labelAlign : 'right',
			labelWidth : 60,
			frame : true,
			defaultType : 'textfield',
			monitorValid : true,
			items : [{
				xtype : 'spacer',
				height : 12
			},{
				fieldLabel : '用户名',
				name : 'username',
				tabIndex : 1,
				width : 150
			}, {
				fieldLabel : '密码',
				name : 'password',
				tabIndex : 2,
				inputType : 'password',
				width : 150
			},{
				xtype : 'checkbox',
				name : 'isPersistent',
				boxLabel: '在此计算机上保存我的信息。',
				labelSeparator: ''
			},{
				xtype : 'spacer',
				height : 12
			}]
		},
		init : function() {
			var mem = divo.find("user_remember")
			if (mem && mem == "1") {
				this.autoLogin()
				return
			} //自动登录
			
			this.loginFormConfig.buttons = [{
				text : '登录',
				tabIndex : 3,
				formBind : true,
				scope : this,
				handler : this.loginHandler
			}]

			this.loginFormConfig.keys = {
				key : Ext.EventObject.ENTER,
				fn : this.loginFormConfig.buttons[0].handler,
				scope : this
			}

			if (this.userCaptcha()) {
				this.loginFormConfig.items.push({				
				    fieldLabel : '验证码',
					name : 'imageCode',
					tabIndex : 3,
					width : 150
				})
				this.loginFormConfig.items.push(new Ext.Panel( {
						border : false,
						autoLoad : {
							url : '/captcha2',
							text : "加载中..."
						}
				}))
			}
			this.loginForm = new Ext.form.FormPanel(this.loginFormConfig)
			this.loginWindow = new Ext.Window({
				title : 'Demo',
				closable : false, // 不允许关闭
				closeAction : 'hide',
				iconCls : 'icon-win',
				autoWidth : false,
				width : 310,
				autoHeight : true,
				modal : true,
				constrain : true,
				resizable : false,
				draggable : false,
				items : [this.loginForm]
			})

			this.loginWindow.on('show', function() {
				this.loginForm.find('name', 'username')[0].focus(true, 100)
			}, this)

			this.loginWindow.on('close', function() {
				this.loginForm.getForm().reset()
			}, this)

			this.loginForm.getForm().clearInvalid()
			this.loginWindow.show()
			
			divo.utils.CapsLock.init()
		},
		clearRememberCookie : function() {
			divo.clearRememberUserInfo()
		},
		loginHandler :function() {
			this.loginWindow.getEl().mask('','x-mask-loading')
			var username = this.loginForm.find('name', 'username')[0].getValue()
			var password = this.loginForm.find('name', 'password')[0].getValue()
			var remember = this.loginForm.find('name', 'isPersistent')[0].getValue()
			var up = divo.utils.base64.encode(username + ':'+ password)
			
			Ext.Ajax.request({
				scope : this,
				url : this.loginUrl+this.getAdminUrl()+this.getCaptchaUrl(),
				method : 'GET',
				headers : {
					Authorization : "Basic " + up
				},
				success : function(response, options) {
					var resp = Ext.decode(response.responseText)
					if (!resp.success) {
						this.loginWindow.getEl().unmask()
						alert(resp.errors.reason)
						return
					}
					Ext.Ajax.defaultHeaders.Authorization = "Basic "+ up
					this.loginWindow.hide()
					this.loginForm.getForm().reset()

					divo.save("user_remember", remember ? "1" : "0")
					if (remember) {
						divo.save("user_loginName", username)
						divo.save("user_password", password)
					} else {
						this.clearRememberCookie()
					}
					
					if (!resp.passwordChanged) {
						this.loginWindow.getEl().unmask()
						var win = new divo.home.ChangePasswordWindow({
							userId:resp.id,
							userName:resp.name,
							userFullName:resp.fullName
						})
						win.show()
						return
					}
					divo.publish("j.showHomepage")
					this.loginWindow.getEl().unmask()
				},
				failure : function(response, options) {
					this.loginWindow.getEl().unmask()
					divo.showError(response.responseText)
					this.loginForm.find('name', 'password')[0].focus(true)
				}
			}) //Ext.Ajax.request
		}, //loginHandler
		autoLogin : function() {
			var username = divo.find("user_loginName")
			var password = divo.find("user_password")
			var up = divo.utils.base64.encode(username + ':'+ password)

			Ext.Ajax.request({
				scope : this,
				url : this.loginUrl+this.getAdminUrl(),
				method : 'GET',
				headers : {
					Authorization : "Basic " + up
				},
				success : function(response, options) {
					var resp = Ext.decode(response.responseText)
					if (!resp.success || !resp.passwordChanged) {
						this.clearRememberCookie()
						location.reload(false)
						return
					}
					Ext.Ajax.defaultHeaders.Authorization = "Basic "+ up
					
					divo.publish("j.showHomepage")
				},
				failure : function(response, options) {
					divo.showError(response.responseText)
					this.clearRememberCookie()
					location.reload(false)
				}
			})
		},
		getAdminUrl : function() {
			var urls = location.href.split("/")
			var qs = urls[urls.length-1]
			if (qs.startsWith("?"))
			    return "?admin="+qs.substr(1,qs.length-1)
			return "?admin="+qs
		},
		getCaptchaUrl : function() {
			if (this.loginForm.getForm().findField('imageCode')) {
				var captcha = document.forms[0].captcha.value
				var captchaInputed = this.loginForm.getForm().findField('imageCode').getValue()
				return "&captcha="+captcha+"|"+captchaInputed
			}
			return ""
		},
		userCaptcha : function() {
			var useCaptcha = false
			Ext.Ajax.request({
				scope : this,
				url : "/appcontext/captcha",
				async : false,
				method : 'GET',
				success : function(response, options) {
					var resp = Ext.decode(response.responseText)
					useCaptcha = resp.useCaptcha
				},
				failure : function(response, options) {
					this.alert(response.responseText)
				}
			})
			return useCaptcha
		}
	}
}()
// EOP
;/**
 * 提示窗口管理
 */
divo.home.toastAlertMgr = function() {

	//-------------- private 方法 ---------------------
	function showXf() {
		//如果是企业会员，尚未审核通过
		Ext.Ajax.request({
			scope : this,
			url : "/xf/corpusers/"+divo.getUserId()+"/info",
			async : true,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.isCorpUser && !resp.audit_passed) {
					divo.toastAlert({message:'您的注册信息还在审核中，<br />点击右上角的“设置”按钮，<br />可以查看和打印企业资料。'})
				}
				showXf1()
			}
		})
	}
	function showXf1() {
		//有“会员审核”菜单项授权的用户，提示有企业会员等待审核
		Ext.Ajax.request({
			scope : this,
			url : "/xf/alarms/corpusers/"+divo.getUserId(),
			async : true,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					divo.toastAlert({message:'有新注册的企业会员等待您的审核。'})
				}
				showXf2()
			}
		})
	}
	function showXf2() {
		//有“产品审核”菜单项授权的用户，提示有消防产品等待审核
		Ext.Ajax.request({
			scope : this,
			url : "/xf/alarms/products/"+divo.getUserId(),
			async : true,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					divo.toastAlert({message:'有新登记的消防产品等待您的核对。'})
				}
				showXf3()
			}
		})
	}
	function showXf3() {
		//对灭火器维修单位的维修证书有效期，提前3个月报警
		Ext.Ajax.request({
			scope : this,
			url : "/xf/alarms/mainters/"+divo.getUserId(),
			async : true,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					divo.toastAlert({message:'贵单位的维修证书有效期即将到期。'})
				}
				showXf4()
			}
		})
	}
	function showXf4() {
		//对产品生产单位的产品的认证有效期，提前3个月报警
		Ext.Ajax.request({
			scope : this,
			url : "/xf/alarms/producers/"+divo.getUserId(),
			async : true,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					divo.toastAlert({message:'贵单位某些消防产品的认证有效期即将到期。'})
				}
				showXf5()
			}
		})
	}
	function showXf5() {
		//对有“检测报告修改申请受理”全局权限的用户，提示有检测报告修改申请等待受理
		Ext.Ajax.request({
			scope : this,
			url : "/xf/alarms/checkreports/application/"+divo.getUserId(),
			async : true,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					divo.toastAlert({message:'有新的检测报告修改申请等待您的受理。'})
				}
			}
		})
	}
	
	
	//-------------- public 方法 ----------------------
	return {
		show : function() {
			if (divo.appContext.hasAppInstalled("xf")) {
				showXf()
			}
		}
	}
}()
//EOP
;var homeApp = function() {
	//------- private 属性 -------------
	var hp
    var jsLoader = new divo.utils.JsLoader()
	
	//------- private 方法 -------------
    function loadJsModuleCallback() {
    	Ext.getBody().unmask()
    }
    
	function loadJsModules() {
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/users/"+divo.getUserId()+"/jsmodules",
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.modules.length>0) {
					Ext.getBody().mask('','x-mask-loading')
					jsLoader.onSuccess = loadJsModuleCallback
					var jsModules = resp.modules.split(",")
					for (var i = 0; i < jsModules.length; i++) {
						var finalLoad = (i==jsModules.length-1)
						if (hp.getAppContext().isDebug)
							jsLoader.load("/media/js/divo-"+jsModules[i]+".js?dev=yes",finalLoad)
						else	
							jsLoader.load("/media/js/divo-"+jsModules[i]+".js?v="+hp.getAppContext().version,finalLoad)
					}
				}
			}
		})
	}
	
	//------- public 方法 --------------
	return {
		init : function() {
			//divo.hideLoading()
			//var msg = Ext.get('loading-msg');
			//msg.hide()
			setTimeout(function(){
			    Ext.get('loading').remove();    
			    Ext.get('loading-mask').fadeOut({remove:true});
			}, 250); 
		
			var urls = location.href.split("/")
			var qs = urls[urls.length-1]
			if (qs.endsWith("register")) {
				var win = new divo.home.RegisterWizWindow()
				win.show()
			} else {
				divo.home.Login.init()
			}	
			
			divo.subscribe("j.showHomepage",this.onShowHomePage,this)
		},
		onShowHomePage : function() {
			hp || (hp = new divo.home.Main({
				id : 'home-app'
			}))
			
			loadJsModules()
			
			divo.home.MenuNavigator.init()
			divo.home.toastAlertMgr.show()
		}
	}
}()

divo.home.Main = Ext.extend(divo.App, {

	initComponent : function() {
		this.initApp()

		Ext.apply(this, {
			layout : 'border',
			defaults : {
				border : false
			},
			border : false,
			items : [{
				region : 'north',
				xtype : 'j.panel.HeaderPanel',
				border : false,
				height : 105
			}, {
				region : 'south',
				contentEl : 'south',
				border : false,
				split : false,
				height : 35,
				collapsible : false,
				margins : '0 0 0 0'				
			},{
				region : 'center',
				layout : 'border',
				defaults : {
					border : false
				},
				border : false,
				items : [{
					region : 'center',
					id : this.myId('center'),
					xtype : 'j.panel.CenterPanel',
					tabPosition : 'bottom',
					notCloseMeHTML : divo.home.MenuNavigator.getMainMenuNavHTML()
				}]
			}]
		})

		divo.home.Main.superclass.initComponent.call(this)
	}
})

// EOP
;/**
 * 主页顶部面板
 * 
 * -------------------------------------------------------------- 
 * 消息： 显示菜单导航界面
 * 
 * 消息名： j.showMenuNavigator
 * 
 * 消息内容： 无 
 * -------------------------------------------------------------- 
 * 消息：
 * 用户选择了某个菜单项
 * 
 * 消息名： j.selectMenuItem
 * 
 * 消息内容： {int} id 菜单项Id {string} name 菜单项名称
 * --------------------------------------------------------------
 */
divo.home.HeaderPanel = Ext.extend(Ext.Panel, {
	id : "j-header-panel",
	data : null,
	initComponent : function() {
		Ext.apply(this, {
			layout : "fit",
			defaults : {
				bodyStyle : "padding: 0px; border: 0px"
			},
			items : [{
				html : this.getHtml()
			}]
		})
		divo.waitFor(this.canSetupLink, this.setupLink, this)
		this.subscribe("j.barItemChanged", this.setupMenuItems, this)

		divo.home.HeaderPanel.superclass.initComponent.call(this);
	},
	getHtml : function() {
		return [
				// '<div id="top_ad">',
				// '<img src="media/images/home/top.png">',
				// '</div>',
				'<br /><div id="header">',
				'	<div id="nav">',
				'		<div id="nav-l">',
				'			<div id="nav-l-t"><a id="home-page" href="#" style="text-decoration: none;color:black">首页</a></div>',
				'		</div>',
				'		<div id="nav-c">',
				'			<div id="nav-c-t">',
				'				欢迎 <img src="media/images/user.gif"><span id="home-user-name"></span>&nbsp;&nbsp;&nbsp;&nbsp;',
				'				<img src="media/images/config.gif"><a id="home-setup" href="#">设置</a>&nbsp;&nbsp;',
				'				<img src="media/images/divo/help.gif"><a id="home-help" href="#">帮助</a>&nbsp;&nbsp;',
				'				<img src="media/images/email.gif"><a id="home-contact" href="#">联系方式</a>&nbsp;&nbsp;',
				'				<img src="media/images/divo/logout.png"><a id="home-logout" href="#">退出</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
				'           </div>', '			<ul id="nav-c-m">', '			</ul>',
				'		</div>', '		<div id="nav-r"></div>', '	</div>', '</div>']
				.join('')
	},
	canSetupLink : function() {
		return Ext.get("header")
	},
	setupLink : function() {
		Ext.get("home-user-name").update(this.getUserFullName())
		this.setupMenuItems()

		var el = Ext.get("header")
		el.on({
			click : {
				stopEvent : false,
				delegate : 'a',
				scope : this,
				fn : function(e, target) {
					var elId = target.id
					if (elId == "home-setup")
						this.showMyProfileWindow(elId)
					if (elId == "home-help")
						this.showHelpWindow(elId)
					if (elId == "home-logout")
						this.logout()
					if (elId == "home-page")
						this.showMainMenu(elId)
					if (elId == "home-contact")
						this.showContact(elId)

					if (elId.startsWith("home-menu")) {
						var ss = elId.split('-')
						divo.publish("j.selectMenuItem", {
							id : ss[2],
							name : target.innerHTML
						})
					}
				}
			}
		}) // 捕获产品检测记录中的修改和删除链接信息
	},
	setupMenuItems : function() {
		var items = null
		Ext.Ajax.request({
			scope : this,
			url : '/users/' + this.getUserId() + "/baritems",
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				items = resp.rows
			}
		})
		if (!items)
			return
		if (items.length == 1 && items[0].id == 0) {
			return
		}
		var el = Ext.get('nav-c-m')
		var html = []
		for (var i = 0; i < items.length; i++) {
			html.push('<li><a id="home-menu-' + items[i].id
					+ '" href="#" style="text-decoration: none;color:black">'
					+ items[i].name + '</a>&nbsp;&nbsp;&nbsp;&nbsp;|</li>')
		}
		el.dom.innerHTML = html.join('')
	},
	showMyProfileWindow : function(elId) {
		if (!this.myProfileWindow)
			this.myProfileWindow = new divo.home.MyProfileWindow({
				cancelAction : "hide",
				closeAction : "hide"
			})
		this.myProfileWindow.show(elId)
	},
	showHelpWindow : function(elId) {
		this.helpWin || (this.helpWin = new divo.home.HelpWindow({}))
		this.helpWin.show(elId)
	},
	logout : function() {
		var isDebug = this.getAppContext().isDebug		
		divo.clearRememberUserInfo()
		if (isDebug)
			location.reload(false)
		else	
			window.location = "http://www.sh70119.com"		
	},
	showMainMenu : function() {
		this.publish("j.showMenuNavigator")
	},
	showContact : function(tid) {
		if (!this.contactWin)
			this.contactWin = new Ext.Window({
				title : '联系方式',
				layout : 'fit',
				cancelAction : "hide", 
				closeAction : "hide",
				width : 500,
				height : 300,
				modal : false,
				closable : true,
				items : [{html: this.getHtmlContent()}]
			})
		this.contactWin.show(tid)
	},
	getHtmlContent : function() {
       return [
'<br /><br /><table class="divo-table"><tbody>',
'<tr>',
'   <td><b>&nbsp;&nbsp;&nbsp;&nbsp;上海市消防局消防产业委员会</b><br /></td>',
'   <td></td>',
'</tr>',
'<tr>',
'   <td><b>&nbsp;&nbsp;&nbsp;&nbsp;地址：</b></td>',
'   <td>上海市河南中路280号508室<br /></td>',
'</tr>',
'<tr>',
'   <td><b>&nbsp;&nbsp;&nbsp;&nbsp;电话：</b></td>',
'   <td>63213535-9147　63213669<br /></td>',
'</tr>',
'<tr>',
'   <td><b>&nbsp;&nbsp;&nbsp;&nbsp;网址：</b></td>',
'   <td><a href="http://www.Sh70119.com/">http://www.Sh70119.com/</a><br /></td>',
'</tr>',
'</tbody></table>'
       ].join("")		
	}
})

Ext.reg("j.panel.HeaderPanel", divo.home.HeaderPanel)

//EOP

;/**
 * 中心区域面板
 */
divo.home.CenterPanel = Ext.extend(divo.layout.TabbedPanel, {
	id : "j-center-panel",
	afterRender : function() {
		divo.home.CenterPanel.superclass.afterRender.call(this)

		this.subscribe("j.selectMenuItem",this.showMenuItem,this)
		this.subscribe("j.showMenuNavigator",this.showMenuNavigator,this)
		
		var p = this.getItem('not-close-me')
		p.on("activate",this.onMenuNavigaterActivated,this)
	},
	showMenuItem : function(subj, msg, data) {
		divo.home.Loader.selectMenu(this, msg)
	},
	showMenuNavigator : function(subj, msg, data) {
		this.activate('not-close-me')
		var f = divo.home.MenuNavigator.scrollToMainMenu
		f.defer(100,f)
	},
	onMenuNavigaterActivated : function() {
		var f = divo.home.MenuNavigator.scrollToMainMenu
		f.defer(100,f)
	}
})

Ext.reg("j.panel.CenterPanel", divo.home.CenterPanel)
// EOP


;/**
 * 菜单导航
 * 
 * 
 * --------------------------------------------------------------
 * 消息：
 * 用户选择了某个菜单项
 * 
 * 消息名：     			
 * j.selectMenuItem	    
 * 
 * 消息内容：
 * {int} id 菜单项Id
 * {string} name 菜单项名称
 * --------------------------------------------------------------
 */
divo.home.MenuNavigator = function() {

	/*---------------- 私有属性 ------------------ */
	var menuDefinitions = []
	
	var mainMenuNavHTML = 
[				
'<div id="menus">',
'	<div id="menus-cb">',
'		<img src="media/js/ext/resources/images/default/s.gif" class="normal-view" title="带描述信息的完整视图"/>',
'		<img src="media/js/ext/resources/images/default/s.gif" class="condensed-view" title="压缩显示的视图" />',
'		<img src="media/js/ext/resources/images/default/s.gif" class="mini-view" title="迷你型视图" />',
'	</div>',
'	',
'    <div id="main-menu"><div id="main-menu-inner"></div></div>',
'    <div id="menu-box"><div id="menu-box-inner">',
'    ',
'	</div></div>',
'</div>'].join('')
	
	var menuItemsTpl = new Ext.XTemplate(
			'<div id="menu-ct">',
			'<tpl for=".">',
			'<div><a name="{id}" id="{id}"></a><h2><div unselectable="on">{title}</div></h2>',
			'<dl>',
			'<tpl for="items">',
			'<dd ext:menuId="{id}"><img title="{text}" src="media/images/screens/{icon}"/>',
			'<div><h4>{text}</h4><p>{desc}</p></div>',
			'</dd>',
			'</tpl>',
			'<div style="clear:left"></div></dl></div>',
			'</tpl>',
			'</div>')

	var mainMenuTpl = new Ext.XTemplate('<tpl for="."><a href="#{id}" hidefocus="on" class="{cls}" id="a4{id}"><img src="media/js/ext/resources/images/default/s.gif">{title}</a></tpl>')

	var mainMenuEl, menuItemsEl
	var bound
	var lastActiveMainMenuId,lastActiveView

	/*---------------- 私有方法 ------------------ */
	//为主菜单添加id属性等
	function setupMenu() {
		if (!getMenuDefinitions()) return false
		
		lastActiveMainMenuId = divo.find('main-menu-active')
		lastActiveView = divo.find('main-menu-view')
		
		if (lastActiveMainMenuId) {
			menuDefinitions[0].cls = null	
		}
		for (var i = 0, c; c = menuDefinitions[i]; i++) {
			c.id = 'menu-' + i
			if (c.id == lastActiveMainMenuId) {
				menuDefinitions[i].cls = 'active'	
			}
		}
		return true
	}
	
	//从后台取得菜单定义
	function getMenuDefinitions() {
		//取得默认模块的全部主菜单
		var menus
		Ext.Ajax.request({
			scope : this,
			url : "users/"+divo.getUserId()+"/workspaces/2/modules/3/menus" ,
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.totalCount>0)
					menus = resp.rows
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		if (!menus) return false
		
		for (var i=0; i < menus.length; i++) {
			var menu = {
				title : menus[i].name,
				items : []
			}
			Ext.Ajax.request({
				scope : this,
				url : "users/"+divo.getUserId()+"/menus/"+menus[i].id+"/items" ,
				async : false,
				method : 'GET',
				success : function(response, options) {
					var resp = Ext.decode(response.responseText)
					for (var j=0; j < resp.rows.length; j++) {
						menu.items.push({
							text : resp.rows[j].name,
							id : resp.rows[j].id,
							icon : resp.rows[j].big_image,
							desc : resp.rows[j].descrip
						})
					}
				},
				failure : function(response, options) {
					this.alert(response.responseText)
				}
			})
			menuDefinitions.push(menu)
		}
		return true
	}

	//计算要滚动到那个位置
	function calcScrollPosition() {
		var found = false, last
		menuItemsEl.select('a[name]', true).each(function(el) {
			last = el
			if (el.getOffsetsTo(menuItemsEl)[1] > -5) {
				activate(el.id)
				found = true
				return false
			}
		})
		if (!found) {
			activate(last.id)
		}
	}

	function bindScroll() {
		menuItemsEl.on('scroll', calcScrollPosition, menuItemsEl, {
			buffer : 250
		})
		bound = true
	}
	
	function unbindScroll() {
		menuItemsEl.un('scroll', calcScrollPosition, menuItemsEl)
		bound = false
	}
	
	//激活对应主菜单项
	function activate(id) {
		Ext.get('a4' + id).radioClass('active')
		divo.save('main-menu-active',id)
	}
	
	//根据id获取菜单项名称
	function getMenuName(id) {
		for (var i = 0; i < menuDefinitions.length; i++) {
			for (var j = 0; j < menuDefinitions[i].items.length; j++) {
				if (menuDefinitions[i].items[j].id == id) {
					return menuDefinitions[i].items[j].text
				}
			}
		}
	}
	
	//滚动菜单项区使与指定主菜单id一致
	function scrollCt(id,unbind) {
		if (!id && lastActiveMainMenuId)
		    id = lastActiveMainMenuId
		    
		if (Ext.getDom(id) && Ext.getDom(id).offsetTop)	{
			var top = Ext.getDom(id).offsetTop
			if (unbind==undefined)
				unbindScroll()
				menuItemsEl.scrollTo('top', top, {
					callback : bindScroll
				})
		}
	}
	
	//设置菜单项区视图类别
	function setCtView(className) {
		Ext.getDom('menus').className = className
		calcScrollPosition.defer(10)
	}
	
	//自动调出最近用过的菜单项
	function showLastMenuItem() {
		Ext.Ajax.request({
			scope : this,
			url : "/users/"+divo.getUserId()+"/menus/items/last",
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.found) {
					divo.publish("j.selectMenuItem", {
						id : resp.menuItemId,
						name : resp.menuItemName
					})
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
	}

	/*---------------- public方法 ------------------ */
	return {
		/**
		 * 初始化
		 */
		init : function() {
			if (!setupMenu()) return

			mainMenuEl = Ext.get('main-menu-inner')
			menuItemsEl = Ext.get('menu-box-inner')

			mainMenuTpl.overwrite(mainMenuEl, menuDefinitions)
			menuItemsTpl.overwrite(menuItemsEl, menuDefinitions)

			menuItemsEl.on('mouseover', function(e, t) {
				if (t = e.getTarget('dd')) {
					Ext.fly(t).addClass('over')
				}
			})
			menuItemsEl.on('mouseout', function(e, t) {
				if ((t = e.getTarget('dd')) && !e.within(t, true)) {
					Ext.fly(t).removeClass('over')
				}
			})
			menuItemsEl.on('click', function(e, t) {
				if ((t = e.getTarget('dd', 5)) && !e.getTarget('a', 3)) {
					//Returns the value of a namespaced attribute from the element's underlying DOM node
					var menuId = Ext.fly(t).getAttributeNS('ext', 'menuId')
					if (menuId) {
						divo.publish("j.selectMenuItem", {
							id : menuId,
							name : getMenuName(menuId)
						})
					} //选择菜单项
				} else if (t = e.getTarget('h2', 3, true)) {
					t.up('div').toggleClass('collapsed')
				}
			})

			mainMenuEl.on('click', function(e, t) {
				e.stopEvent()
				if ((t = e.getTarget('a', 2)) && bound) {
					var id = t.href.split('#')[1]
					Ext.get(t).radioClass('active')
					scrollCt(id)					
					divo.save('main-menu-active',id)
				}
			})

			Ext.get('menus-cb').on('click', function(e) {
				var img = e.getTarget('img', 2)
				if (img) {
					setCtView(img.className)
					divo.save('main-menu-view',img.className)
				}
			})

			bindScroll()
			
			if (lastActiveView) {
				setCtView(lastActiveView)
			}
			if (lastActiveMainMenuId) {
				scrollCt(lastActiveMainMenuId,false)
			}
			
			//showLastMenuItem()
		},
		scrollToMainMenu : function() {
			scrollCt(divo.find('main-menu-active'),false)
		},
		/**
		 * 返回界面构造用HTML
		 */
		getMainMenuNavHTML : function() {
			return mainMenuNavHTML
		}
	}

}()
// EOP
;/**
 * 菜单项对应JS程序加载器
 */
divo.home.Loader = function() {
	/* ----------------------- private变量 ----------------------- */
	var maxPanelNum = 10
    var jsLoader = new divo.utils.JsLoader();

	/* ----------------------- private方法 ----------------------- */
	// 选择菜单项
	function selectMenu(mainPanel, msg) {
		loadApp(mainPanel, msg)
		
		var items = mainPanel.items.items
		if (items.length == maxPanelNum + 2) {
			var menuId = items[1].id // [0]为占位Panel
			var tab = mainPanel.getItem(menuId)
			mainPanel.remove(tab)
		} //限制最多能打开的面板,避免浏览器因过载崩溃
	}

	// 加载菜单对应的程序
	function loadApp(mainPanel, msg) {
		var menuId = msg.id
		var title = msg.title?msg.title:msg.name
		
		var ok = true
		var objName,loadOnDemand,imageUrl
		Ext.Ajax.request({
			scope : this,
			url : "/menus/items/"+menuId+"/main",
			async : false,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (!resp.success) {
					alert("加载菜单项时出错（请查看log.txt，获得详细错误信息）")
					return
				}
				objName = resp.objName
				loadOnDemand = resp.loadOnDemand
				imageUrl = resp.imageUrl
			},
			failure : function(response, options) {
				divo.showError(response.responseText)
				ok = false
			}
		});
		if (!ok) return
		
		if (!msg.close) 
			saveLastItem(menuId)
		
        var callback = function() {
		    var oMain
		    eval('oMain = ' + objName)
		    
		    var n = 0
            while (!oMain && n<2) {
			   alert("点击[确定]后继续")
			   eval('oMain = ' + objName)
			   n++
            } //js加载后执行函数体需要时间
            
			if (oMain.isPopup) {
				oMain.init()
				return
			} // 独立弹出窗口
			
			var tabId = 'mp-'+menuId+(msg.tabId?"-"+msg.tabId:"")
			var oldTabPanel = mainPanel.getItem(tabId)
			var image = '<img src="media/images/menus/'+imageUrl+'"/>'

			if (msg.close) {
				mainPanel.remove(oldTabPanel)
				oldTabPanel = null
			}
			
			var newTabPanel = mainPanel.openDynamicTab(tabId, image + title)
			if (!oldTabPanel)
			    oMain.init(newTabPanel,menuId,msg.extraInfo)
        }

        if (loadOnDemand) {
			jsLoader.onSuccess = callback;
			//通过dev参数避免客户端缓冲，参见middle_http_auth.py
			jsLoader.load("/media/debug/"+objName+".js?dev=yes",true);
        } else {
        	callback()
        }
	}
	
	//记忆最近使用过的菜单项
	function saveLastItem(menuId) {
		divo.saveProfileAsync({
			userId:divo.getUserId(),
			msgCode:'last.menu.item',
			msgValue:menuId
		})
	}
	
	/* ----------------------- public方法 ----------------------- */
	return {

		/**
		 * 选择菜单项
		 */
		selectMenu : function(mainPanel, msg) {
			selectMenu(mainPanel, msg)
		},
		/**
		 * 返回当前活动标签页对应的菜单项Id
		 */
		getActiveMenuItemId : function(mainPanel) {
			var p = mainPanel.getActiveTab()
			if (!p.id.startsWith('mp-')) return null
			
			return p.id.split("-")[1]
		}

	} // return

}()

// EOP

;/**
 * 修改登录密码表单
 */
divo.home.ChangePasswordForm = Ext.extend(Ext.form.FormPanel, {
	userId : null,
	userName : null,
	initComponent : function() {
		this.user = this.getAppContext().user
		
		Ext.apply(this, {
			//baseCls : "x-plain",
			labelWidth : 150,
			defaultType : "textfield",
			labelAlign : "right",
			autoScroll : true,
			bodyStyle : "padding:10px",
			items : [{
				xtype : 'panel',
				border : false,
				bodyStyle : 'padding-left:10px;padding-bottom:20px',
				html : '<p class="common-text"><b>请注意</b>：密码长度必须大于6位，可以由英文字母、阿拉伯数字等组成，区分大小写。</p>'
			}, {
				fieldLabel : "新密码",
				name : "new_password",
				inputType : "password",
				width : 250
			}, {
				fieldLabel : "再次输入新密码",
				inputType : "password",
				name : "new_password2",
				width : 250
			}]
		})

		divo.waitFor(this.canInitFocus,this.initFocus,this)
		divo.home.ChangePasswordForm.superclass.initComponent.call(this)

	},
	canInitFocus : function() {
	    var o = this.getForm()
	    if (o)
	    	var fld = this.getForm().findField("new_password")
	    if (fld)
    		divo.utils.CapsLock.init()
		return fld
	},
	//public
	initFocus : function() {
		var f = this.getForm().findField("new_password")
		f.focus.defer(100, f)
	},
	// public
	save : function(callbackOnSuccess) {
		var item = this.getForm().getObjectValues()
		Ext.getBody().mask('正在保存，请稍候...', 'x-msg-loading')

		Ext.Ajax.request({
			scope : this,
			url : "users/" + this.userId + "/password/new",
			method : 'PUT',
			jsonData : item,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				Ext.getBody().unmask()
				
				if (!resp.success) {
					this.say(resp.errors.reason)
				} else {
					var up = divo.utils.base64.encode(this.userName + ':'
							+ options.jsonData.new_password)
					Ext.Ajax.defaultHeaders.Authorization = "Basic " + up
					Ext.MessageBox.alert('提示',"新密码已经保存")
					callbackOnSuccess.call()
				}
			},
			failure : function(response, options) {
				Ext.getBody().unmask()
			    this.alert(response.responseText)
			}
		})
	}
})

Ext.reg("divo.home.ChangePasswordForm", divo.home.ChangePasswordForm)
// EOP
;/**
 * 用户初次登录修改密码窗口
 * 
 * 
 * --------------------------------------------------------------
 * 消息：
 * 初始密码设置完毕，显示主页
 * 
 * 消息名：     			
 * j.showHomepage   
 * 
 * 消息内容：
 * 无
 * --------------------------------------------------------------
 */
divo.home.ChangePasswordWindow = Ext.extend(Ext.Window, {
	userId : null,
	userName : null,
	userFullName : null,
	cancelAction : "destroy",
	closeAction : "destroy",
	initComponent : function() {
		Ext.apply(this,{
			title : this.userFullName+'，您好，请设置登录用新密码:',
			width : 580,
			height : 250,
			modal : true,
			closable : false,
			maximizable : false,
			minimizable : false,
			layout : "fit",
			buttons : [{
				text : "确定",
				handler : this.onSave,
				scope : this
			}],
			items : {
				xtype : "divo.home.ChangePasswordForm",
				userId : this.userId,
				userName : this.userName
			},
			keys : {
				key : Ext.EventObject.ENTER,
				fn : this.onSave,
				scope : this
			}
		})
		
		divo.home.ChangePasswordWindow.superclass.initComponent.call(this);
	},
	onSave : function() {
		this.items.itemAt(0).save(this.onSaveSucccess.createDelegate(this))
	},
	onSaveSucccess : function() {
		this.close()
		divo.publish("j.showHomepage")
	}
})
// EOP
;/**
 * 快捷菜单设置表单
 */
divo.home.ShortcutForm = Ext.extend(Ext.form.FormPanel, {
	userId : null,
	initComponent : function() {
		
		Ext.apply(this, {
			baseCls : "x-plain",
			labelWidth : 75,
			labelAlign : "right",
			autoScroll : true,
			bodyStyle : "padding:0",
			items : [{
				xtype : "itemselector",
				name : 'shortcut',
				hideLabel : true,
				fromStore:this.getFromDataStore(),
				toStore:this.getToDataStore(),
				msWidth:250,
				msHeight:200,
				valueField : "id",
				displayField : "name",
				toLegend:"快捷菜单",
				fromLegend:"候选菜单"
			}]
		})

		divo.home.ShortcutForm.superclass.initComponent.call(this)

	},
	getFromDataStore : function() {
		return new Ext.data.Store({					 
			autoLoad: true,
			proxy : new Ext.data.HttpProxy( {
				method : 'GET',
				url : "/users/"+this.getUserId()+"/shortcuts/avaiable"
			}),
			reader: this.getReader(),
			sortInfo:{field:'name', direction:'ASC'}
		})		
	},
	getToDataStore : function() {
		return new Ext.data.Store({					 
			autoLoad: true,
			proxy : new Ext.data.HttpProxy( {
				method : 'GET',
				url : "/users/"+this.getUserId()+"/shortcuts"
			}),
			reader: this.getReader()
		})		
	},
	getReader : function() {
		return new Ext.data.JsonReader({
			idProperty:'id',
			root:'rows',
			fields: [
				{name: 'id', type: 'int'},
				{name: 'name', type: 'string'}
			]
		})		
	},
	//public
	hasChanged : function() {
		try {
			if (!this.getForm()) return false
			var currentValues = this.getForm().getObjectValues().shortcut
		}catch(e){
			return false
		}
		if (!this.oldValues) {
			this.oldValues = currentValues
			return false
		}
		return currentValues!=this.oldValues
	},
	//public
	save : function(callbackOnSuccess) {
		var itemStr = this.getForm().getObjectValues()
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/users/"+this.getUserId()+"/shortcuts/order",
			async : false,
			method : 'PUT',
			jsonData : {idList:itemStr.shortcut},
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		})
		if (ok) {
			this.say("快捷菜单已经保存")
			this.oldValues = null
			callbackOnSuccess.call()
		}
	}
})

Ext.reg("divo.home.ShortcutForm", divo.home.ShortcutForm)
// EOP

;/**
 * 横条菜单设置表单
 */
divo.home.BarItemForm = Ext.extend(Ext.form.FormPanel, {
	userId : null,
	initComponent : function() {
		
		Ext.apply(this, {
			baseCls : "x-plain",
			labelWidth : 75,
			labelAlign : "right",
			autoScroll : true,
			bodyStyle : "padding:0",
			items : [{
				xtype : "itemselector",
				name : 'baritem',
				hideLabel : true,
				fromStore:this.getFromDataStore(),
				toStore:this.getToDataStore(),
				msWidth:250,
				msHeight:200,
				valueField : "id",
				displayField : "name",
				toLegend:"已选菜单项",
				fromLegend:"候选菜单项"
			}]
		})

		divo.home.BarItemForm.superclass.initComponent.call(this)
	},
	getFromDataStore : function() {
		return new Ext.data.Store({					 
			autoLoad: true,
			proxy : new Ext.data.HttpProxy( {
				method : 'GET',
				url : "/users/"+this.getUserId()+"/baritems/avaiable"
			}),
			reader: this.getReader(),
			sortInfo:{field:'name', direction:'ASC'}
		})		
	},
	getToDataStore : function() {
		return new Ext.data.Store({					 
			autoLoad: true,
			proxy : new Ext.data.HttpProxy( {
				method : 'GET',
				url : "/users/"+this.getUserId()+"/baritems"
			}),
			reader: this.getReader()
		})		
	},
	getReader : function() {
		return new Ext.data.JsonReader({
			idProperty:'id',
			root:'rows',
			fields: [
				{name: 'id', type: 'int'},
				{name: 'name', type: 'string'}
			]
		})		
	},
	//public
	hasChanged : function() {
		try {
			if (!this.getForm()) return false
			var currentValues = this.getForm().getObjectValues().baritem
		}catch(e){
			return false
		}
		if (!this.oldValues) {
			this.oldValues = currentValues
			return false
		}
		return currentValues!=this.oldValues
	},
	//public
	save : function(callbackOnSuccess) {
		var itemStr = this.getForm().getObjectValues()
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/users/"+this.getUserId()+"/baritems/order",
			async : false,
			method : 'PUT',
			jsonData : {idList:itemStr.baritem},
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		})
		if (ok) {
			this.oldValues = null
			this.say("顶部菜单已经调整")
			callbackOnSuccess.call()
		}
	}
})

Ext.reg("divo.home.BarItemForm", divo.home.BarItemForm)
// EOP

;/**
 * 快捷菜单设置面板
 *
 * --------------------------------------------------------------
 * 消息：
 * 用户变动了快捷菜单
 * 
 * 消息名：     			
 * j.shortcutChanged	    
 * 
 * 消息内容：
 * 无
 * --------------------------------------------------------------
 * 
 */
divo.home.ShortcutPanel = Ext.extend(Ext.Panel, {
	initComponent : function() {
		Ext.apply(this, {
			tbar : this.getTbar(),
			layout : "fit",
			items : [this.createShortcutForm()]
		})

		this.on('show',this.startMonitoring,this)
		this.on('hide',this.stopMonitoring,this)
		this.on('beforedestroy',this.stopMonitoring,this)
		
		divo.home.ShortcutPanel.superclass.initComponent.call(this)
	},
	createShortcutForm : function() {
		this.shortForm = new divo.home.ShortcutForm()
		return this.shortForm
	},
	getTbar : function() {
		return ["->", {
			text : '<img src="'+divo.iconUrl + 'divo/save.gif"/>保存',
			handler : this.saveShortcut,
			scope : this
		}]
	},
	saveShortcut : function() {
		var me = this
		this.shortForm.save(function() {
			me.publish("j.shortcutChanged")
		})
	},
    startMonitoring : function(){
        if(!this.bound){
            this.bound = true
	        Ext.TaskMgr.start({
	            run : this.bindHandler,
	            interval : 200,
	            scope: this
	        })
        }    
    },
    bindHandler : function() {
        if(!this.bound){
            return false // stops binding
        }
    	if (this.shortForm.hasChanged()) {
    		this.saveShortcut()
    	}
    },
    stopMonitoring : function(){
        this.bound = false
    }
})

Ext.reg("divo.home.ShortcutPanel", divo.home.ShortcutPanel)
//EOP
;/**
 * 横条菜单设置面板
 *
 * --------------------------------------------------------------
 * 消息：
 * 用户变动了顶部菜单
 * 
 * 消息名：     			
 * j.barItemChanged	    
 * 
 * 消息内容：
 * 无
 * --------------------------------------------------------------
 * 
 */
divo.home.BarItemPanel = Ext.extend(Ext.Panel, {
	initComponent : function() {
		Ext.apply(this, {
			layout : "fit",
			items : [this.createBarItemForm()]
		})
		
		this.on('show',this.startMonitoring,this)
		this.on('hide',this.stopMonitoring,this)
		this.on('beforedestroy',this.stopMonitoring,this)
		
		divo.home.BarItemPanel.superclass.initComponent.call(this)
	},
	createBarItemForm : function() {
		this.shortForm = new divo.home.BarItemForm()
		return this.shortForm
	},
	saveBarItem : function() {
		var me = this
		this.shortForm.save(function() {
			me.publish("j.barItemChanged")
		})
	},
    startMonitoring : function(){
        if(!this.bound){
            this.bound = true
	        Ext.TaskMgr.start({
	            run : this.bindHandler,
	            interval : 200,
	            scope: this
	        })
        }    
    },
    bindHandler : function() {
        if(!this.bound){
            return false // stops binding
        }
    	if (this.shortForm.hasChanged()) {
    		this.saveBarItem()
    	}
    },
    stopMonitoring : function(){
        this.bound = false
    }
	
})

Ext.reg("divo.home.BarItemPanel", divo.home.BarItemPanel)
//EOP
;/**
 * 设置面板（这是容器）
 */
divo.home.MyProfilePanel = Ext.extend(Ext.TabPanel, {
	initComponent : function() {
		Ext.apply(this, {
			defaults : {
				bodyStyle : "padding:10px"
			},
			activeTab : 0,
			minTabWidth : 115,
			tabWidth : 135,
			enableTabScroll : true,
			items : this.getMyProfilePanels()
		});
		divo.home.MyProfilePanel.superclass.initComponent.call(this)
	},
	getMyProfilePanels : function() {
		var isCorpUser = false
//		if (this.getAppContext().hasAppInstalled("xf")) {
//			isCorpUser = this.isCorpUser()
//		}
		var ps = []
		ps.push({
			xtype : "divo.home.ProfileDetailPanel",
			title : '<img src="media/images/user.gif"/>个人资料'
		})
		if (isCorpUser)
			ps.push({
				xtype : "divo.home.CorpInfoPanel",
				title : '<img src="media/images/version.gif"/>企业资料'
			})
		if (this.isCheckerUserAndAuditPassed() && !this.isSecondaryCorpUser())
		    var corpInfo = 
			ps.push({
				xtype : "divo.home.CheckUserGridPanel",
				title : '<img src="media/images/role.gif"/>操作员'
			})
		ps.push({
			xtype : "divo.home.BarItemPanel",
			title : '<img src="media/images/comment.gif"/>顶部菜单'
		})
		return ps
	}
})

Ext.reg("divo.home.MyProfilePanel", divo.home.MyProfilePanel)
// EOP

;/**
 * 用户个人设置窗口
 */
divo.home.MyProfileWindow = Ext.extend(Ext.Window, {
	cancelAction : "destroy",
	closeAction : "destroy",
	initComponent : function() {
		Ext.apply(this,{
			title : "设置",
			stateful : true,
			stateId : "home-my-profile-win",
			width : 680,
			height : 470,
			modal : true,
			closable : true,
			maximizable : true,
			minimizable : false,
			buttons : [{
				text : "关闭",
				handler : this.onCancel,
				scope : this
			}],
			layout : "fit",
			items : {
				xtype : "divo.home.MyProfilePanel"
			}
		});
		
		divo.home.MyProfileWindow.superclass.initComponent.call(this);
	}

});
//EOP
;/**
 * 个人信息修改面板
 */
divo.home.ProfileDetailPanel = Ext.extend(Ext.Panel, {
	initComponent : function() {
		this.user = this.getAppContext().user
		
		Ext.apply(this, {
			tbar : this.getTbar(),
			layout : "card",
			activeItem : 0,
			items : [{
				autoScroll : true,
				xtype : "panel",
				items : this.createUserForm()
			}, this.createPasswordForm()]
		})
		divo.home.ProfileDetailPanel.superclass.initComponent.call(this)
	},
	createUserForm : function() {
		this.userForm = new divo.home.UserForm({
			showButtons : false,
			formType : "both",  //默认为both
			showTitle : false
		})
		return this.userForm
	},
	createPasswordForm : function() {
		this.passwordForm = new divo.home.ChangePasswordForm({
			userId:this.user.id,
			userName:this.user.name}
		)
		return this.passwordForm
	},
	getTbar : function() {
		return ["->", {
			text : '<img src="'+divo.iconUrl + 'locked.gif"/>修改密码',
			handler : this.showChangePasswordTab,
			scope : this
		}, "-", {
			text : '<img src="'+divo.iconUrl + 'divo/user.gif"/>修改个人信息',
			handler : this.editUserProfile,
			disabled  : this.user.id == 1, //admin用户不能修改个人信息
			scope : this
		}, "-", {
			text : '<img src="'+divo.iconUrl + 'divo/save.gif"/>保存',
			handler : this.saveUserProfile,
			scope : this
		}, "-", {
			text : '<img src="'+divo.iconUrl + 'divo/save.gif"/>保存',
			handler : this.savePassword,
			scope : this
		}, "-", {
			text : '<img src="'+divo.iconUrl + 'divo/reset.gif"/>取消',
			handler : this.resetForm,
			scope : this
		}, "-", {
			text : '<img src="'+divo.iconUrl + 'divo/delete.png"/>清除记忆信息',
			handler : this.deleteMyProfile,
			scope : this
		}]
	},
	resetForm : function() {
		this.getLayout().setActiveItem(0)
		this.userForm.onCancel()
		this.showButtons([0, 1, 2, 3, 10, 11]) 
		//传递的参数含义: 工具条上包括'->'和'-'，共0-11十个控件。
	},
	saveUserProfile : function() {
		this.userForm.updateUser(this.resetForm.createDelegate(this))
	},
	savePassword : function() {
		this.passwordForm.save(this.resetForm.createDelegate(this))
	},
	showChangePasswordTab : function() {
		this.getLayout().setActiveItem(1)
		this.showButtons([0, 7, 8, 9])
		this.passwordForm.initFocus()
	},
	editUserProfile : function() {
		this.userForm.onEdit()
		this.showButtons([0, 5, 8, 9])
	},
	showButtons : function(arr) {
		var items = this.getTopToolbar().items
		for (var i = 0; i < items.length; i++) {
			items.get(i).hide()
		}
		for (var i = 0; i < arr.length; i++) {
			items.get(arr[i]).show()
		}
	},
	render : function() {
		divo.home.ProfileDetailPanel.superclass.render.apply(this, arguments)
		this.showButtons([0, 1, 2, 3, 10, 11])
	},
	deleteMyProfile : function() {
		Ext.MessageBox.confirm('确认', '清除记忆信息后，如窗口位置和大小、页面布局等会恢复到默认状态，要继续吗？', 
				this.onDeleteMyProfile.createDelegate(this))
	},				
	onDeleteMyProfile : function(btn) {
		if (btn !== 'yes')
		    return
		
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/myprofile/"+this.getUserId(),
			async : false,
			method : 'DELETE',
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		})
		if (ok) {
			this.say("操作成功")
		}
	}
})

Ext.reg("divo.home.ProfileDetailPanel", divo.home.ProfileDetailPanel)
//EOP
;/**
 * 用户信息表单
 */
divo.home.UserForm = Ext.extend(divo.form.DivoForm, {
	mode : "new",
	orgId : null,
	initComponent : function() {
		this.user = this.getAppContext().user

		Ext.apply(this, {
			//formType : "editOnly",
			showButtons : false,
			showTitle : false,
			title : "用户信息表单",
			alternateRows : false,
			labelClass : "label"
		});
		divo.home.UserForm.superclass.initComponent.call(this);

		this.fieldDefinitions = this.getFormDefinitions();
	},
	refreshForm : function(mode) {
		if (mode) {
			this.mode = mode;
		}
		this.setFieldDefinitions(this.getFormDefinitions());
	},
	getFormDefinitions : function() {
		return [{
			field : "id",
			label : "用户Id",
			readOnly : true
		},{
			field : "full_name",
			label : "姓名",
			required : true
		},{
			field : "name",
			label : "用户名",
			readOnly : true
		},{
			field : "email",
			label : "电子邮箱"
		}]
	},
	render : function() {
		divo.home.UserForm.superclass.render.apply(this, arguments);
		this.showUser()
	},
	showUser : function() {
		this.setItem({
			'id':this.user.id,
			'name':this.user.name,
			'full_name':this.user.fullName,
			'email':this.user.email
		});
	},
	userEmailValidate : function(v) {
		var t = /^\w+([-+.]\w+)*@\w+([-.]\\w+)*\.\w+([-.]\w+)*$/;
		return t.test(v)
	},
	updateUser : function(callbackOnSuccess) {
		var item = this.getUpdatedItem();
		if (item.email && !this.userEmailValidate(item.email)){
		    this.say("请填写正确的邮箱格式, 如 fff@123.com！")
	 	    return false
		}
		
		Ext.Ajax.request({
			scope : this,
			url : "users/"+this.user.id,
			waitMsg : "正在提交...",
			async : true,
			method : 'PUT',
			jsonData : item,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					this.say('修改成功')
					
					//只允许修改姓名
					this.user.fullName = item.full_name
					this.getAppContext().user.fullName = item.full_name
					this.getAppContext().user.email = item.email
					this.showUser()
					//TODO: 更新LogoutPanel中显示的姓名。
					
					callbackOnSuccess.call()
				} else {
					this.errors = []
					for (var obj in resp.errors) {
						this.errors.push(eval('resp.errors.'+obj))
					}
					this.say(this.errors[0])
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		});
	}
});

Ext.reg("divo.home.UserForm", divo.home.UserForm);
// EOP
;/**
 * 加载数据
 */
divo.home.LoadDataForm = Ext.extend(Ext.form.FormPanel, {
	initComponent : function() {
		this.isDebug = this.getAppContext().isDebug
		
		var btns1 = [{
					items : [this.btnMenuItem = new Ext.Button({
						handler : this.onLoadMenuItems,
						text : "加载“菜单项”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-menu-item"></div><span class="common-text" id="data-load-menu-item-text"></span><br /><br />'
				}, {
					items : [this.btnGridView = new Ext.Button({
						handler : this.onLoadGridViews,
						text : "加载“网格视图”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-grid-view"></div><span class="common-text" id="data-load-grid-view-text"></span><br /><br />'
				}, {
					items : [this.btnPortalView = new Ext.Button({
						handler : this.onLoadPortalViews,
						text : "加载“门户状态”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-portalviews"></div><span class="common-text" id="data-load-portalviews-text"></span><br /><br />'
				}, {
					items : [this.btnGlobal = new Ext.Button({
						handler : this.onLoadGlobalPermissions,
						text : "加载“全局权限”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-globals"></div><span class="common-text" id="data-load-globals-text"></span><br /><br />'
				}]
		
        var btns2 = [{
					items : [this.btnRestApi = new Ext.Button({
						handler : this.onLoadRestApis,
						text : "加载“REST服务API文档”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-restapis"></div><span class="common-text" id="data-load-restapis-text"></span><br /><br />'
				}, {
					items : [this.btnWidget = new Ext.Button({
						handler : this.onLoadWidgets,
						text : "加载“客户端器件文档”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-widgets"></div><span class="common-text" id="data-load-widgets-text"></span><br /><br />'
				}]
				
		var btns3 = [{
					items : [this.btnOther = new Ext.Button({
						handler : this.onLoadOthers,
						text : "加载“其他数据”",
						scope : this
					})]
				}, {
					html : '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
				}, {
					html : '<div id="data-load-others"></div><span class="common-text" id="data-load-others-text"></span><br /><br />'
				}]			
				
		Ext.apply(this, {
			autoScroll : true,
			bodyStyle : "padding:20px",
			items : [{
				layout : "table",
				defaults : {
					border : false
				},
				border : false,
				layoutConfig : {
					columns : 3
				},
				items : this.isDebug? btns1.concat(btns2).concat(btns3): btns1.concat(btns3)
				}]
		})

		divo.home.LoadDataForm.superclass.initComponent.apply(this,
				arguments)
	},
	onLoadMenuItems : function() {
		this.loadAndshowProgess('data-load-menu-item','/data/menuitems',this.btnMenuItem)
	},
	onLoadGridViews : function() {
		this.loadAndshowProgess('data-load-grid-view','/data/gridviews',this.btnGridView)
	},
	onLoadGlobalPermissions : function() {
		this.loadAndshowProgess('data-load-globals','/data/globals',this.btnGlobal)
	},
	onLoadRestApis : function() {
		this.loadAndshowProgess('data-load-restapis','/data/restapis',this.btnRestApi)
	},
	onLoadWidgets : function() {
		this.loadAndshowProgess('data-load-widgets','/data/widgets',this.btnWidget)
	},
	onLoadPortalViews : function() {
		this.loadAndshowProgess('data-load-portalviews','/data/portalviews',this.btnPortalView)
	},
	onLoadOthers : function() {
		this.loadAndshowProgess('data-load-others','/data/others',this.btnOther)
	},
	loadAndshowProgess : function(id,url,btn) {
		btn.disable()
		var p = new Ext.ProgressBar({
			width : 300,
			renderTo : id
		});
		p.on('update', function(val) {
			Ext.fly(id+'-text').dom.innerHTML += '.';
		});
		Ext.fly(id+'-text').update('加载中...');
		p.wait()
		Ext.Ajax.request({
			scope : this,
			url : url,
			async : true,
			method : 'PUT',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					p.destroy()
					btn.enable()
					Ext.fly(id+'-text').update('完成');
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
				p.destroy()
				btn.enable()
				Ext.fly(id+'-text').update('失败');
			}
		});
	}
})

Ext.reg('divo.home.LoadDataForm', divo.home.LoadDataForm)
// EOP

;/**
 * REST API 文档显示网格
 */
divo.home.RestApiGridPanel = Ext.extend(divo.panel.SmartGridDetailPanel, {
	initComponent : function() {
 		this.action = new Ext.ux.grid.RowActions({
            header:'操作',
            autoWidth:false, 
            width:60,
            actions:[{
                iconCls:'icon-source',
                tooltip:'查看源代码'
            },{
                iconCls:'icon-doc',
                tooltip:'查看详细文档'
            }]
        })
		this.action.on('action',this.onAction,this)
	
		Ext.apply(this, {
			url : "/rest/api",
			fieldDefinitions : this.getListDefinitions(),
			gridConfig : {
				gridIdOfView : 'home-rest-grid', 
				filter : true,
				showTooltip : true,
				export2Excel : true,
				plugins : [this.action]
			},
			autoScroll : true
		})
		
		divo.home.RestApiGridPanel.superclass.initComponent.apply(this,arguments)
	},
	onAction : function(grid, record, action, row, col) {
		if (action=='icon-doc') {
			var detail = record.data.http_method+" "+record.data.url
		    this.showWindow("详细文档 - "+detail,"/rest/api/"+record.data.module_name+"/"+record.data.method_name+"/doc")
		}
		if (action=='icon-source') {
			var detail = record.data.module_name
		    this.showWindow("源代码 - "+detail,"/rest/api/"+record.data.module_name+"/source")
		}    
	},
	//重写
	createGridMeta : function() {
		return this.getGridMeta(this.fieldDefinitions,this.isLookup?null:[this.action])
	},
	getListDefinitions : function() {
		return [{
			name : 'id'
		},{
			name : 'http_method',
			header : 'HTTP方法',
			searchable : true,
			type : 'string'
		},{
			name : 'url',
			header : 'URL格式',
			searchable : true,
			type : 'string'
		},{
			name : 'descrip',
			header : '描述',
			searchable : true,
			type : 'string'
		},{
			name : 'module_name',
			header : '模块名',
			searchable : true,
			type : 'string'
		},{
			name : 'method_name',
			header : '函数名',
			searchable : true,
			type : 'string'
		}]
	},
	showWindow : function(title,url) {
		var auth = "?auth="+Ext.Ajax.defaultHeaders.Authorization

		if (this.winIframe) {
			this.winIframe.el.dom.src = url+auth
			this.win.setTitle(title)
			this.win.show()
			return
		}	
			
		this.winIframe = new divo.layout.IFrameComponent( {
			id : 'win-main',
			autoScroll : true,
			url : url+auth
		});

		this.win = new Ext.Window( {
			title : title,
			iconCls : 'icon-win',
			stateful : true,
			stateId : 'restapi-win',
			height : 440,
			width : 622,
			resizable : true,
			modal : false,
			autoScroll : true,
			closeAction : 'hide',
			layout : 'fit',
			closable : true,
			items : this.winIframe,
			maximizable : true
		})
		this.win.show()
	}
})

Ext.reg('divo.home.RestApiGridPanel', divo.home.RestApiGridPanel);
// EOP

;/**
 * 客户端器件文档显示网格
 */
divo.home.WidgetGridPanel = Ext.extend(divo.panel.SmartGridDetailPanel, {
	initComponent : function() {
 		this.action = new Ext.ux.grid.RowActions({
            header:'操作',
            autoWidth:false, 
            width:60,
            actions:[{
                iconCls:'icon-source',
                tooltip:'查看源代码'
            }]
        })
		this.action.on('action',this.onAction,this)
	
		Ext.apply(this, {
			url : "/widgets",
			fieldDefinitions : this.getListDefinitions(),
			gridConfig : {
				gridIdOfView : 'home-widget-grid', 
				filter : true,
				showTooltip : true,
				export2Excel : true,
				plugins : [this.action]
			},
			autoScroll : true
		})
		
		divo.home.WidgetGridPanel.superclass.initComponent.apply(this,arguments)
	},
	onAction : function(grid, record, action, row, col) {
		if (action=='icon-source') {
			var detail = record.data.xtype
		    this.showWindow("源代码 - "+detail,"/widgets/"+detail+"/source")
		}    
	},
	//重写
	createGridMeta : function() {
		return this.getGridMeta(this.fieldDefinitions,this.isLookup?null:[this.action])
	},
	getListDefinitions : function() {
		return [{
			name : 'id'
		},{
			name : 'xtype',
			header : '类名',
			searchable : true,
			type : 'string'
		},{
			name : 'descrip',
			header : '描述',
			searchable : true,
			type : 'string'
		}]
	},
	showWindow : function(title,url) {
		var auth = "?auth="+Ext.Ajax.defaultHeaders.Authorization

		if (this.winIframe) {
			this.winIframe.el.dom.src = url+auth
			this.win.setTitle(title)
			this.win.show()
			return
		}	
			
		this.winIframe = new divo.layout.IFrameComponent( {
			id : 'win-main',
			autoScroll : true,
			url : url+auth
		});

		this.win = new Ext.Window( {
			title : title,
			stateful : true,
			stateId : 'widget-win',
			iconCls : 'icon-win',
			height : 440,
			width : 622,
			resizable : true,
			modal : false,
			autoScroll : true,
			closeAction : 'hide',
			layout : 'fit',
			closable : true,
			items : this.winIframe,
			maximizable : true
		})
		this.win.show()
	}
})

Ext.reg('divo.home.WidgetGridPanel', divo.home.WidgetGridPanel);
// EOP

;/**
 * 帮助面板
 */
divo.home.HelpPanel = Ext.extend(Ext.TabPanel, {
	initComponent : function() {
		var isDebug = this.getAppContext().isDebug
		var items = [{
				title : "帮助文档",
				html : "<h2>(链接到外网上的帮助文档)</h2>"
			}, {
				title : "加载数据",
				xtype : "divo.home.LoadDataForm"
			}]
		if (isDebug) {	
            items.push({
				title : "REST服务API文档",
				xtype : "divo.home.RestApiGridPanel"
			})
			items.push({
				title : "客户端器件文档",
				xtype : "divo.home.WidgetGridPanel"
			})
		}	
		Ext.apply(this, {
			defaults : {
				bodyStyle : "padding:10px"
			},
			activeTab : 0,
			minTabWidth : 115,
			tabWidth : 135,
			enableTabScroll : true,
			items : items
		});
		divo.home.HelpPanel.superclass.initComponent.call(this);
	}
});
Ext.reg("divo.home.HelpPanel", divo.home.HelpPanel);
//EOP

;/**
 * 帮助窗口
 */
divo.home.HelpWindow = function(config) {
	config = Ext.applyIf(config, {
		title : "帮助",
		height : 500,
		width : 700,
		modal : true,
		stateful : true,
		stateId : 'help-win',
		closable : true,
		closeAction : "hide",
		cancelAction : "hide",
		maximizable : true,
		minimizable : false,
		buttons : [{
			text : "关闭",
			handler : this.onCancel,
			scope : this
		}],
		layout : "fit",
		items : {
			xtype : "divo.home.HelpPanel"
		}
	});
	divo.home.HelpWindow.superclass.constructor.call(this, config);
};
Ext.extend(divo.home.HelpWindow, Ext.Window, {});
//EOP
;/**
 * 用户自注册向导窗口
 */
divo.home.RegisterWizWindow = Ext.extend(Ext.ux.Wiz, {
	stateful : false,
	initComponent : function() {
		Ext.apply(this, {
			id : 'user-register-wiz',
			title : '会员注册',
			width : 800,
			height : 500,

        	headerConfig : {
            	title : ''    
        	},
			
			cardPanelConfig : {
				defaults : {
					baseCls : 'x-small-editor',
					//bodyStyle : 'padding:10px 15px 5px 15px;background-color:none;', // 上 右 下 左
					autoScroll : true,
					border : false
				}
			},

			cards : [

			new divo.home.RegisterWizCard1({id : 'card1'}),
			new divo.home.RegisterWizCard2({id : 'card2'}),
			new divo.home.RegisterWizCard3({id : 'card3'}),
			
			new Ext.ux.Wiz.Card({
				id : 'card4',
				title : '请等待审核',
				monitorValid : true,
				items : [{
					border : false,
					//bodyStyle : 'background:nonepadding-bottom:10px',
					html : '<br />&nbsp;&nbsp;&nbsp;&nbsp;请点击“结束”按钮完成注册，我们会尽快处理您的注册申请。'
				}]
			})]
		})

		this.on('finish', this.onFinished, this)

		divo.home.RegisterWizWindow.superclass.initComponent.apply(this,
				arguments)
	},
	onFinished : function() {
		var wd = this.getWizardData()
		var data = {}
		for (var prop in wd.card2)
		     data[prop] = wd.card2[prop]
		for (var prop in wd.card3)
		     data[prop] = wd.card3[prop]
		     
		data.is_member = eval(data.is_member)
		data.is_trustful = eval(data.is_trustful)
		data.is_recommended = eval(data.is_recommended)
		     
		Ext.getBody().mask('正在保存注册信息，请稍候...', 'x-msg-loading')
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/xf/register",
			async : true,
			method : 'POST',
			jsonData  : data,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				Ext.getBody().unmask()
				alert("注册已完毕! 请进入登录页面。\n登录后点击页面右上角的“设置”链接，打印所填写的信息资料表，加盖公章后，\n与《通知》要求的其他资料一并递交至上海消防协会消防产业委员会进行审核。\n" +
						"感谢您的注册！")
				window.location = "http://www.sh70119.com"		
			},
			failure : function(response, options) {
				ok = false
				Ext.getBody().unmask()
				this.alert(response.responseText)
			}
		})
		return ok
	}
});/**
 * 用户自注册向导窗口: 第1页
 */
divo.home.RegisterWizCard1 = Ext.extend(Ext.ux.Wiz.Card, {
	initComponent : function() {
		this.card1_content = [
'<table align="center" border="0" cellpadding="0" cellspacing="0" width="750">',
'<tbody><tr><td height="20"></td></tr>',
'<tr><td>',
'<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;欢迎您注册上海市消防产业信息系统，在您注册成为上海市消防产业信息系统用户前，请先认真阅读本协议.',
'<p>&nbsp;</p>',
'<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>会员注册与服务协议</b><p>',
'<p>&nbsp;</p>',
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为规范用户在上海市消防产业信息系统网站上的行为，明确本系统与用户之间的权利义务,特签署本注册协议。具体条款如下：</p>',
'<p>&nbsp;</p>',
'<p><b>第一条 协议双方</b><p>',
'<p>1.1、本协议中的“系统”指上海市消防产业信息系统。本系统由上海市消防协会消防产业委员会主办（下称委员会）。</p>',
'<p>1.2、本协议中的“用户”指申请并注册成为上海市消防产业信息系统注册会员的单位。</p>',
'<p>1.3、用户同意此在线注册服务协议之效力如同用户亲自签字、盖章的书面条款一样，自用户注册成功之日起便对用户具有法律约束力。</p>',
'<p>&nbsp;</p>',
'<p><b>第二条 上海市消防产业信息系统功能与服务</b></p>',
'<p>2.1、本系统功能主要由以下三部分组成：</p>',
'<p>1）消防企业登记企业信息；</p>',
'<p>2）消防企业发布消防产品信息；</p>',
'<p>3）消防工程检测公司制作检测报告三部分组成。</p>',
'<p>2.2、本系统可以提供检索消防产品的原产地及来源地的服务，可以防止假冒伪劣消防产品在市场流行，保证消防工作、消防工程的质量。</p>',
'<p>2.3、本系统可以监督消防产品的生产、流通，消防工程的施工、检测、验收等流程进行，保护消防企业利益。</p>',
'<p>&nbsp;</p>',
'<p><b>第三条 用户身份保证</b></p>',
'<p>3.1、用户承诺并保证自己是具有完全民事行为能力和完全民事权利能力的法人、经济实体和其他组织。用户在此保证所填写的用户信息是真实、准确、完</p>',
'<p>整、及时的，并且没有任何引人误解或者虚假的陈述。</p>',
'<p>3.2、用户应根据本系统的要求及时提供相应的身份证明等资料，否则本系统有权拒绝向该会员或用户提供相关服务。</p>',
'<p>3.3、用户承诺将及时更新其用户信息以维持该等信息的有效性。</p>',
'<p>&nbsp;</p>',
'<p><b>第四条  用户的帐号、密码及安全性</b></p>',
'<p>4.1、用户注册成功后将得到一个用户名和密码，用户凭用户名和密码享受本系统提供的服务。用户有权修改其密码。</p>',
'<p>4.2、用户应妥善保管其用户名与密码，未尽到到妥善保管义务的，应承担以其用户名进行活动所产生的法律责任。<p>',
'<p>4.3、非经委员会书面同意，用户名和密码不得擅自转让或者授权他人使用。<p>',
'<p>4.4、用户若发现任何非法使用用户帐号或存在安全漏洞的情况，请立即通告本系统。<p>',
'<p>&nbsp;</p>',
'<p><b>第五条　计费与缴费</b><p>',
'<p>5.1、系统为用户提供有偿服务或免费服务。有偿服务的具体价格及收费方式将标明在相应的页面上，用户可以根据需要自行选择。用户选择某项服务即代表着同意接受所选服务的费用。<p>',
'<p>5.2、用户停止使用本系统提供的一项或多项服务，取消已经定制的服务的，仍应按照已经定制的服务收费标准向系统支付服务费。该标准以用户定制该服务时的相应页面上明示的计费及收费方式为准。<p>',
'<p>&nbsp;</p>',
'<p><b>第六条　协议条款的修改</b><p>',
'<p>6.1、系统修改本协议及相关服务条款的，该修改内容将在主要页面上提示。用户不同意该修改的，可以取消相应服务；用户继续享用服务的，视为接受服务条款的变更。<p>',
'<p>6.2、修改本协议及相关服务条款涉及服务价格的，在修改之前用户定制的具体服务，本系统将按照已达成的协议执行至已缴费用的服务期期满。<p>',
'<p>&nbsp;</p>',
'<p><b>第七条　用户的其他义务</b><p>',
'<p>7.1、用户同意本系统公示其注册与发布的信息。<p>',
'<p>7.1、用户同意本系统通过电子邮件方式与其联系。对由于用户向本公司提供的联络方式有误以及用户用于接受本公司邮件的电子邮箱安全性、稳定性不佳而导致的后果，用户自行承担责任。<p>',
'<p>7.2、用户保证不会利用技术或其他手段破坏及扰乱本公司网站以及本公司其他客户的网站。<p>',
'<p>7.3、用户保证其使用本系统时遵从国家、地方法律法规、行业惯例和社会公共道德，不利用本系统提供的服务进行存储、发布、传播如下信息和内容：<p>',
'<p>违反国家法律法规政策的任何内容（信息）；违反国家规定的政治宣传和/或新闻信息；涉及国家秘密和/或安全的信息；封建迷信和/或淫秽、色情、下流的信息或教唆犯罪的信息；博彩有奖、赌博游戏；违反国家民族和宗教政策的信息；防碍互联网运行安全的信息；侵害他人合法权益的信息和/或其他有损于社会秩序、社会治安、公共道德的信息或内容。用户同时承诺不得为他人发布上述',
'不符合国家规定和/或本服务条款约定的信息内容提供任何便利，包括但不限于设置URL、BANNER链接等。',
'用户同意本系统有权在用户违反上述时终止向其提供服务并不予退还任何款项，因用户上述行为给本公司造成损失的，用户应予赔偿。<p>',
'<p>&nbsp;</p>',
'<p><b>第八条　不可抗力</b><p>',
'<p>8.1、因不可抗力或者其他意外事件，使得本条款履行不可能、不必要或者无意义的，遭受不可抗力、意外事件的一方不承担责任。 <p>',
'<p>不可抗力、意外事件是指不能预见、不能克服并不能避免且对一方或双方当事人造成重大影响的客观事件，包括但不限于自然灾害如洪水、地震、瘟疫流行和风暴等以及社会事件如战争、动乱、政府行为等。 <p>',
'<p>8.2、用户同意鉴于互联网的特殊性，黑客攻击、互联网连通中断或者系统故障等属于不可抗力，由此给用户或者第三方造成的损失不应由系统承担。 <p>',
'<p>&nbsp;<p>',
'<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上海市消防协会消防产业委员会对以上条款有最终解释权。<p>',
'</td></tr>',
'</tbody></table><br /><br />'
].join("")

		Ext.apply(this, {
			title : '阅读协议',
			defaultType : 'textfield',
			//monitorValid : true, 会导致按下一步后无效
			labelWidth : 150,
			layout : 'autotableform',
			items : [{
				hideLabel : true,
				xtype : 'formHtml',
				value : this.card1_content
			}, {
				xtype : "ux-radiogroup",
				id : this.myId('agree'),
				fieldLabel : "",
				labelSeparator : "",
				name : "agree",
				horizontal : true,
				radios : [{
					value : "1",
					boxLabel : "我同意此协议"
				}, {
					value : "0",
					boxLabel : "我不同意此协议"
				}]
		}]}),

		divo.waitFor(this.canGetCmp,this.doGetCmp,this)

		divo.home.RegisterWizCard1.superclass.initComponent.apply(this,
				arguments)
	},
	canGetCmp : function() {
		return Ext.getCmp(this.myId('agree'))
	},
	doGetCmp : function() {
		Ext.getCmp(this.myId('agree')).on('check',function (o,v) {
			this.fireEvent('clientvalidation',this,v=="1")
		},this)

		this.fireEvent('clientvalidation',this,false) //使下一步disabled
	}

})
// EOP
;/**
 * 用户自注册向导窗口: 第2页
 */
divo.home.RegisterWizCard2 = Ext.extend(Ext.ux.Wiz.Card, {
	initComponent : function() {
		Ext.apply(this, {
			title : '填写会员帐号资料',
			defaultType : 'textfield',
			monitorValid : true,
			labelWidth : 150,
			items : [{
				xtype : 'panel',
				border : false,
				bodyStyle : 'background:none;padding-bottom:10px',
				html : '带 (' + divo.required + ') 号栏位为必填项。'
			}, {
				xtype : "ux-radiogroup",
				fieldLabel : "企业类型",	
				name : "corpType",
				horizontal : true,
				radios : [{
					value : "producer",
					boxLabel : "产品生产单位",
					checked : true
				}, {
					value : "projecter",
					boxLabel : "工程施工单位"
				}, {
					value : "checker",
					boxLabel : "消防系统检测单位"
				}, {
					value : "mainter",
					boxLabel : "灭火器维修单位"
				}, {
					value : "advisor",
					boxLabel : "咨询单位"
				}]
			},{
				name : 'corpName',
				fieldLabel : divo.required + '企业名称',
				allowBlank : false,
				width : 400
			},{
				name : 'name',
				fieldLabel : divo.required + '用户名',
				allowBlank : false,
				validator : function(v) {
					var t = /^[a-zA-Z0-9_\- ]+$/;
					return t.test(v);
				}
			}, {
				xtype : 'panel',
				border : false,
				bodyStyle : 'background:none;padding-left:155px;padding-bottom:10px',
				html : "即系统的登录名，一经注册成功之后将不可以随意更改，只能输入英文、数字。"
			}, {
				fieldLabel : divo.required + "登录密码",
				allowBlank : false,
				name : "password",
				minLength : 6,
				inputType : "password"
			}, {
				xtype : 'panel',
				border : false,
				bodyStyle : 'background:none;padding-left:155px;padding-bottom:10px',
				html : "密码长度必须大于6位，可以由英文字母、阿拉伯数字等组成，区分大小写。"
			}, {
				fieldLabel : divo.required + "再输一次登录密码",
				allowBlank : false,
				inputType : "password",
				name : "password2"
			},{
				name : 'userEmail',
				fieldLabel : divo.required + '电子邮件',
				allowBlank : false,
				vtype : 'email'
			}]
		}),

		this.subscribe("divo.wiz.previous",this.onPrevious,this)
		this.on("beforecardhide", this.validateCard, this)

		divo.home.RegisterWizCard2.superclass.initComponent.apply(this,
				arguments)
	},
	onPrevious : function(subj, msg, data) {
		if (msg.currentCard==1)
			this.lastAction = 'previous'
	},
	validateCard : function(o) {
		if (this.lastAction == 'previous') {
			this.lastAction = null
			return true
		}
		
		var data = this.getForm().getObjectValues()
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/xf/register/user",
			async : false,
			method : 'GET',
			params : data,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (!resp.success) {
					ok = false
					this.getForm().markInvalid(resp.errors)
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		})
		if (ok) this.refreshCard3(data)
		return ok
	},
	refreshCard3 : function(data) {
		this.publish("divo.register.card2Finish",{sender:this.id,card2Data:data})
	}

})
// EOP
;/**
 * 用户自注册向导窗口: 第3页
 */
divo.home.RegisterWizCard3 = Ext.extend(Ext.ux.Wiz.Card, {
	initComponent : function() {
		Ext.apply(this, {
			title : '填写企业资料',
			defaultType : 'textfield',
			monitorValid : true,
			labelWidth : 150,
			items : [{
				xtype : 'panel',
				border : false,
				bodyStyle : 'background:none;padding-bottom:30px;',
				html : '带 (' + divo.required + ') 号栏位为必填项。'
			},{
				xtype : 'combo',
				fieldLabel : divo.required + '所在地区',
				name : 'corpArea',
				allowBlank : false,
				emptyText : '',
				mode : 'remote',
				editable : false,
				triggerAction : 'all',
				forceSelection : true,
				valueField : 'name',
				displayField : 'name',
				store : new Ext.data.Store({
					id : 'id',
					proxy : new Ext.data.HttpProxy({
						url : "/xf/areas",
						method : 'GET'
					}),
					reader : new Ext.data.JsonReader({
						root : 'rows',
						fields : ['id', 'name']
					})
				})
			}, {
				fieldLabel : divo.required + "法定代表人",
				name : "corporation",
				allowBlank : false,
				maxLength : 20,
				width : 300
			}, {
				fieldLabel : divo.required + "法定代表人电话",
				name : "corporation_tele",
				allowBlank : false,
				maxLength : 40,
				width : 300
			}, {
				fieldLabel : divo.required + "主要负责人",
				allowBlank : false,
				name : "leader",
				maxLength : 20,
				width : 300
			}, {
				fieldLabel : divo.required + "主要负责人电话",
				allowBlank : false,
				name : "leader_tele",
				maxLength :40,
				width : 180
			}, {
				fieldLabel : divo.required + "主要负责人手机号",
				allowBlank : false,
				name : "leader_mobile",
				maxLength :40,
				width : 180
			},{
				fieldLabel : divo.required + "单位电话号码",
				allowBlank : false,
				name : "tele",
				maxLength : 40,
				width : 180
			}, {
				fieldLabel : divo.required + "单位传真号码",
				allowBlank : false,
				name : "fax",
				maxLength : 40,
				width : 300
			}, {
				fieldLabel : divo.required + "邮编",
				allowBlank : false,
				name : "post_code",
				maxLength : 40,
				width : 300
			}, {
				fieldLabel : divo.required + "工商注册地址",
				allowBlank : false,
				name : "register_address",
				width : 300
			}, {
				fieldLabel : divo.required + "实际经营地址",
				allowBlank : false,
				name : "address",
				width : 300
			}, {
				xtype : 'numberfield',
				fieldLabel : divo.required + "注册资金(万元)",
				allowBlank : false,
				name : "register_money",
				decimalPrecision : 0,
				allowNegative : false,
				width : 50,
				maxLength : 10
			}, {
				xtype : 'numberfield',
				fieldLabel : divo.required + "员工人数",
				allowBlank : false,
				name : "employee_count",
				decimalPrecision : 0,
				allowNegative : false,
				width : 50,
				maxLength : 5
			}, {
				xtype : 'numberfield',
				fieldLabel : divo.required + "技术员工人数",
				allowBlank : false,
				name : "tech_count",
				decimalPrecision : 0,
				maxLength : 5,
				allowNegative : false,
				width : 50
			}, {
				xtype : "ux-radiogroup",
				fieldLabel : "上海消防协会消防产业委员会会员单位",
				name : "is_member",
				horizontal : true,
				radios : [{
					value : true,
					boxLabel : "是",
					checked : true
				}, {
					value : false,
					boxLabel : "否"
				}]

			}, {
				xtype : "ux-radiogroup",
				fieldLabel : "上海市消防产业诚信承诺单位",
				name : "is_trustful",
				horizontal : true,
				radios : [{
					value : true,
					boxLabel : "是",
					checked : true
				}, {
					value : false,
					boxLabel : "否"
				}]

			}, {
				fieldLabel : divo.required + "维修证书号",
				allowBlank : true,
				name : "certificate_number",
				maxLength : 80,
				width : 300
			}, {
				fieldLabel : divo.required + "维修证书有效期",
				allowBlank : true,
			    name : "certificate_date",
	         	format : 'Y.m.d',
				width : 300
			},  {
				xtype : "ux-radiogroup",
				fieldLabel : "上海市消防行业推荐产品单位",
				name : "is_recommended",
				horizontal : true,
				radios : [{
					value : true,
					boxLabel : "是",
					checked : true
				}, {
					value : false,
					boxLabel : "否"
				}]

			},{
				fieldLabel : divo.required + "资质证书编号",
				allowBlank : true,
				name : "qualification_number",
				maxLength : 80,
				width : 300
			},{
				xtype : "ux-radiogroup",
				fieldLabel : "工程资质等级",
				name : "project_grade",
				horizontal : true,
				radios : [{
					value : "一级",
					boxLabel : "一级",
					checked:true
				}, {
					value : "二级",
					boxLabel : "二级"
				}, {
					value : "三级",
					boxLabel : "三级"
				}, {
					value : "临时",
					boxLabel : "临时"
				}]
								
			},{
				xtype : "ux-radiogroup",
				fieldLabel : "消防专业承包资质等级",
				name : "specialty_grade",
				horizontal : true,
				radios : [{
					value : "一级",
					boxLabel : "一级",
					checked:true
				}, {
					value : "二级",
					boxLabel : "二级"
				}, {
					value : "三级",
					boxLabel : "三级"
				}, {
					value : "临时",
					boxLabel : "临时"
				}]
								
			},{
				xtype : "ux-radiogroup",
				fieldLabel : "消防设计资质等级",
				name : "design_grade",
				horizontal : true,
				radios : [{
					value : "一级",
					boxLabel : "一级",
					checked:true
				}, {
					value : "二级",
					boxLabel : "二级"
				}, {
					value : "没有",
					boxLabel : "没有"
				}]
			},{
				xtype : "ux-radiogroup",
				fieldLabel : "检测资质等级",
				name : "grade",
				horizontal : true,
				radios : [{
					value : "一级",
					boxLabel : "一级",
					checked : true
				}, {
					value : "二级",
					boxLabel : "二级"
				}]

			},{
				fieldLabel : divo.required + "计量认证标志编号",
				allowBlank : true,
				name : "auth_code",
				maxLength : 80,
				width : 300
			},{
				fieldLabel : "单位编号",
				name : "code",
				width : 300
			},{
				fieldLabel : "备注",
				name : "note",
				width : 300
			}]
		})
		
		this.subscribe("divo.register.card2Finish",this.onCard2Finished,this)
		this.subscribe("divo.wiz.previous",this.onPrevious,this)
		this.on("beforecardhide", this.validateCard, this)

		divo.home.RegisterWizCard3.superclass.initComponent.apply(this,
				arguments)
	},
	onPrevious : function(subj, msg, data) {
		if (msg.currentCard==2)
			this.lastAction = 'previous'
	},
	onCard2Finished : function(subj, msg, data) {
		this.corpType = msg.card2Data.corpType
		if (msg.card2Data.corpType == "producer") {
			this.getForm().findField('qualification_number').hide()
		    this.getForm().findField('certificate_number').hide()
			this.getForm().findField('certificate_date').hide()
			this.getForm().findField('project_grade').hide()
			this.getForm().findField('specialty_grade').hide()
			this.getForm().findField('design_grade').hide()
			this.getForm().findField('grade').hide()
			this.getForm().findField('is_recommended').show()
			this.getForm().findField('code').hide()
			this.getForm().findField('auth_code').hide()
			}
	   if (msg.card2Data.corpType=="projecter") {
	        this.getForm().findField('is_recommended').hide()
	        this.getForm().findField('certificate_number').hide()
			this.getForm().findField('certificate_date').hide()
			this.getForm().findField('qualification_number').show()
	        this.getForm().findField('project_grade').show()
			this.getForm().findField('specialty_grade').show()
			this.getForm().findField('design_grade').show()	
			this.getForm().findField('grade').hide()
			this.getForm().findField('code').hide()
			this.getForm().findField('auth_code').hide()
	        }
	   if (msg.card2Data.corpType=="checker") {
	   	    this.getForm().findField('qualification_number').hide()
	        this.getForm().findField('is_recommended').hide()
	        this.getForm().findField('certificate_number').hide()
			this.getForm().findField('certificate_date').hide()
	        this.getForm().findField('project_grade').hide()
			this.getForm().findField('specialty_grade').hide()
			this.getForm().findField('design_grade').hide()	
			this.getForm().findField('grade').show()
			this.getForm().findField('code').show()
			this.getForm().findField('auth_code').show()
           }
	  if (msg.card2Data.corpType=="mainter") {
	  	    this.getForm().findField('qualification_number').hide()
		    this.getForm().findField('is_recommended').hide()
	        this.getForm().findField('certificate_number').show()
			this.getForm().findField('certificate_date').show()
	        this.getForm().findField('project_grade').hide()
			this.getForm().findField('specialty_grade').hide()
			this.getForm().findField('design_grade').hide()	
			this.getForm().findField('grade').hide()
			this.getForm().findField('code').hide()
			this.getForm().findField('auth_code').hide()
           }
     	   if (msg.card2Data.corpType=="advisor") {
	        this.getForm().findField('is_recommended').hide()
	        this.getForm().findField('certificate_number').hide()
			this.getForm().findField('certificate_date').hide()
			this.getForm().findField('qualification_number').show()
	        this.getForm().findField('project_grade').show()
			this.getForm().findField('specialty_grade').show()
			this.getForm().findField('design_grade').show()	
			this.getForm().findField('grade').hide()
			this.getForm().findField('code').hide()
	        }      
	},
	validateCard : function(o) {
		if (this.lastAction == 'previous') {
			this.lastAction = null
			return true
		}
		
		var data = this.getForm().getObjectValues()
		data.corpType = this.corpType
		
		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "xf/register/corporation",
			async : false,
			method : 'GET',
			params : data,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (!resp.success) {
					ok = false
					this.say("输入数据验证未通过，请检查。")
					this.getForm().markInvalid(resp.errors)
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		})
		return ok
	}

})
// EOP
;/**
 * 企业资料显示面板
 */
divo.home.CorpInfoPanel = Ext.extend(Ext.Panel, {
	corpUserInfo : null,
	initComponent : function() {
		this.corpInfo = this.getCorpInfo()
		this.productFormPanelId = this.myId("form")
		this.productGridPanelId = this.myId("grid")
		Ext.apply(this, {
			autoScroll : true,
			layout : 'border',
			defaults : {
				border : false
			},
			border : false,
			items : [{
				region : 'center',
				layout : 'fit',
				autoScroll : true,
				xtype : "panel",
				items : this.createCorpInfoForm()
			},{
				region : 'south',
				layout : 'fit',
				autoScroll : true,
				stateful : true,
				stateId : this.myId('north'),
				height : 50,
				split : true,
				items : [{
					id : this.productGridPanelId,
					xtype : "divo.home.CorpInfoHeaderForm",
					corpId : this.corpInfo.id,
					corpName : this.corpInfo.name,
					readOnly : this.readOnly
				}]
			}]
			
		})
		
		divo.home.CorpInfoPanel.superclass.initComponent.apply(this,arguments)
	},
	createCorpInfoForm : function() {
		return new divo.home.CorpInfoForm({
			showCurrentCorpUserInfo : true
		})
	}
})

Ext.reg('divo.home.CorpInfoPanel', divo.home.CorpInfoPanel)
// EOP

;/**
 * 企业资料显示用表单(用于xf模块）
 */
divo.home.CorpInfoForm = Ext.extend(divo.form.DivoBasicForm, {
	showCurrentCorpUserInfo : false,
	initComponent : function() {
		Ext.apply(this, {
			autoScroll : true,
			formType : "readOnly",
			tableClass : "j-grid-table",
			readOnly : true,
			alternateRows : true,
			labelClass : "label",
			labelWidth : "30%",
			fieldDefinitions : this.getFormDefinitions()
		})
		divo.home.CorpInfoForm.superclass.initComponent.call(this)
	},
	render : function() {
		divo.home.CorpInfoForm.superclass.render.apply(this, arguments)
		if (this.showCurrentCorpUserInfo)
			this.setItem(this.getCorpInfo())
	},
	getFormDefinitions : function() {
		if (this.isCheckerUser()) {
			return this.getFormDefinitionsOfChecker()
		}
		if (this.isProjecterUser()) {
			return this.getFormDefinitionsOfProjecter()
		}
		if (this.isProducerUser()) {
			return this.getFormDefinitionsOfProducer()
		}
		if (this.isMainterUser()) {
			return this.getFormDefinitionsOfMainter()
		}
		if (this.isAdvisorUser()) {
			return this.getFormDefinitionsOfAdvisor()
		}
	},
	getFormDefinitionsOfChecker : function() {
		return ([{
			field : "name",
			label : "企业名称"
		},{
			field : "py",
			label : "拼音助记符"
		}, {
			field : 'grade',
			label : '检测资质等级'
		}, {
			field : "area_name",
			label : "所在地区"
		}, {
			field : "corporation",
			label : "法定代表人"
		},{
			field : 'corporation_tele',
			label : '法定代表人电话'
		},{
			field : 'leader',
			label : '主要负责人'
		},{
			field : 'leader_tele',
			label : '主要负责人电话'
		},{
			field : 'leader_mobile',
			label : '主要负责人手机号'
		},{
			field : 'tele',
			label : '单位电话号码'
		},{
			field : 'fax',
			label : '单位传真号码'
		},{
			field : 'post_code',
			label : '邮编'
		},{
			field : 'register_address',
			label : '工商注册地址'
		},{
			field : 'address',
			label : '实际经营地址'
		},{
			field : 'register_money',
			label : '注册资金(万元)',
			dataType : 'int'
		},{
			field : 'employee_count',
			label : '员工人数',
			dataType : 'int'
		},{
			field : 'tech_count',
			label : '技术员工人数',
			dataType : 'int'
		},{
			field : 'is_member',
			label : '上海消防协会消防产业委员会会员单位',
			dataType : 'boolean'
		},{
			field : 'is_trustful',
			label : '上海市消防产业诚信承诺单位',
			dataType : 'boolean'
		},{
			field : 'auth_code',
			label : '计量认证标志编号'
		},{
			field : 'note',
			label : '备注',
			dataType : 'string'
		}])
	},
	getFormDefinitionsOfMainter : function() {
		return ([{
			field : "name",
			label : "企业名称"
		},{
			field : "py",
			label : "拼音助记符"
		},{
			field : 'certificate_number',
			label : '维修证书号'
		},{
			field : 'certificate_date',
			label : '维修证书有效期'
		}, {
			field : "area_name",
			label : "所在地区"
		}, {
			field : "corporation",
			label : "法定代表人"
		},{
			field : 'corporation_tele',
			label : '法定代表人电话'
		},{
			field : 'leader',
			label : '主要负责人'
		},{
			field : 'leader_tele',
			label : '主要负责人电话'
		},{
			field : 'leader_mobile',
			label : '主要负责人手机号'
		},{
			field : 'tele',
			label : '单位电话号码'
		},{
			field : 'fax',
			label : '单位传真号码'
		},{
			field : 'post_code',
			label : '邮编'
		},{
			field : 'register_address',
			label : '工商注册地址'
		},{
			field : 'address',
			label : '实际经营地址'
		},{
			field : 'register_money',
			label : '注册资金(万元)',
			dataType : 'int'
		},{
			field : 'employee_count',
			label : '员工人数',
			dataType : 'int'
		},{
			field : 'tech_count',
			label : '技术员工人数',
			dataType : 'int'
		},{
			field : 'is_member',
			label : '上海消防协会消防产业委员会会员单位',
			dataType : 'boolean'
		},{
			field : 'is_trustful',
			label : '上海市消防产业诚信承诺单位',
			dataType : 'boolean'
		},{
			field : 'note',
			label : '备注',
			dataType : 'string'
		}])
	},
	getFormDefinitionsOfProducer : function() {
		return ([{
			field : "name",
			label : "企业名称"
		}, {
			field : "py",
			label : "拼音助记符"
		}, {
			field : "area_name",
			label : "所在地区"
		}, {
			field : "corporation",
			label : "法定代表人"
		},{
			field : 'corporation_tele',
			label : '法定代表人电话'
		},{
			field : 'leader',
			label : '主要负责人'
		},{
			field : 'leader_tele',
			label : '主要负责人电话'
		},{
			field : 'leader_mobile',
			label : '主要负责人手机号'
		},{
			field : 'tele',
			label : '单位电话号码'
		},{
			field : 'fax',
			label : '单位传真号码'
		},{
			field : 'post_code',
			label : '邮编'
		},{
			field : 'register_address',
			label : '工商注册地址'
		},{
			field : 'address',
			label : '实际经营地址'
		},{
			field : 'register_money',
			label : '注册资金(万元)',
			dataType : 'int'
		},{
			field : 'employee_count',
			label : '员工人数',
			dataType : 'int'
		},{
			field : 'tech_count',
			label : '技术员工人数',
			dataType : 'int'
		},{
			field : 'is_member',
			label : '上海消防协会消防产业委员会会员单位',
			dataType : 'boolean'
		},{
			field : 'is_trustful',
			label : '上海市消防产业诚信承诺单位',
			dataType : 'boolean'
		},{
			field : 'is_recommended',
			label : '上海市消防行业推荐产品单位',
			dataType : 'boolean'
		},{
			field : 'note',
			label : '备注',
			dataType : 'string'
		}])
	},
	getFormDefinitionsOfProjecter : function() {
		return ([{
			field : "name",
			label : "企业名称"
		},{
			field : "py",
			label : "拼音助记符"
		}, {
			field : 'qualification_number',
			label : '资质证书编号'
		},{
			field : 'project_grade',
			label : '工程资质等级'
		},{
			field : 'specialty_grade',
			label : '消防专业承包资质等级'
		},{
			field : 'design_grade',
			label : '消防设计资质等级'
		}, {
			field : "area_name",
			label : "所在地区"
		}, {
			field : "corporation",
			label : "法定代表人"
		},{
			field : 'corporation_tele',
			label : '法定代表人电话'
		},{
			field : 'leader',
			label : '主要负责人'
		},{
			field : 'leader_tele',
			label : '主要负责人电话'
		},{
			field : 'leader_mobile',
			label : '主要负责人手机号'
		},{
			field : 'tele',
			label : '单位电话号码'
		},{
			field : 'fax',
			label : '单位传真号码'
		},{
			field : 'post_code',
			label : '邮编'
		},{
			field : 'register_address',
			label : '工商注册地址'
		},{
			field : 'address',
			label : '实际经营地址'
		},{
			field : 'register_money',
			label : '注册资金(万元)',
			dataType : 'int'
		},{
			field : 'employee_count',
			label : '员工人数',
			dataType : 'int'
		},{
			field : 'tech_count',
			label : '技术员工人数',
			dataType : 'int'
		},{
			field : 'is_member',
			label : '上海消防协会消防产业委员会会员单位',
			dataType : 'boolean'
		},{
			field : 'is_trustful',
			label : '上海市消防产业诚信承诺单位',
			dataType : 'boolean'
		},{
			field : 'note',
			label : '备注',
			dataType : 'string'
		}])
	},
	getFormDefinitionsOfAdvisor : function() {
		return ([{
			field : "name",
			label : "企业名称"
		},{
			field : "py",
			label : "拼音助记符"
		}, {
			field : 'qualification_number',
			label : '资质证书编号'
		},{
			field : 'project_grade',
			label : '工程资质等级'
		},{
			field : 'specialty_grade',
			label : '消防专业承包资质等级'
		},{
			field : 'design_grade',
			label : '消防设计资质等级'
		}, {
			field : "area_name",
			label : "所在地区"
		}, {
			field : "corporation",
			label : "法定代表人"
		},{
			field : 'corporation_tele',
			label : '法定代表人电话'
		},{
			field : 'leader',
			label : '主要负责人'
		},{
			field : 'leader_tele',
			label : '主要负责人电话'
		},{
			field : 'leader_mobile',
			label : '主要负责人手机号'
		},{
			field : 'tele',
			label : '单位电话号码'
		},{
			field : 'fax',
			label : '单位传真号码'
		},{
			field : 'post_code',
			label : '邮编'
		},{
			field : 'register_address',
			label : '工商注册地址'
		},{
			field : 'address',
			label : '实际经营地址'
		},{
			field : 'register_money',
			label : '注册资金(万元)',
			dataType : 'int'
		},{
			field : 'employee_count',
			label : '员工人数',
			dataType : 'int'
		},{
			field : 'tech_count',
			label : '技术员工人数',
			dataType : 'int'
		},{
			field : 'is_member',
			label : '上海消防协会消防产业委员会会员单位',
			dataType : 'boolean'
		},{
			field : 'is_trustful',
			label : '上海市消防产业诚信承诺单位',
			dataType : 'boolean'
		},{
			field : 'note',
			label : '备注',
			dataType : 'string'
		}])
	}
})
Ext.reg("divo.home.CorpInfoForm", divo.home.CorpInfoForm)

// EOP
;/**
 * 查看企业资料PDF窗口
 */
divo.home.CorpInfoPDFWindow = Ext.extend(Ext.Window, {
	cancelAction : "hide",  
	closeAction : "hide",
	corpId : null,
	corpName : null,
	initComponent : function() {
		var url
		if (this.isCheckerUser()) {
			url = "/xf/checkers/"+this.corpId+"/pdf"
		}
		if (this.isProjecterUser()) {
			url = "/xf/projecters/"+this.corpId+"/pdf"
		}
		if (this.isProducerUser()) {
			url = "/xf/producers/"+this.corpId+"/pdf"
		}
		if (this.isMainterUser()) {
			url = "/xf/mainters/"+this.corpId+"/pdf"
		}
		if (this.isAdvisorUser()) {
			url = "/xf/advisors/"+this.corpId+"/pdf"
		}
		
		Ext.apply(this,{
			title : '企业资料'+(this.corpName?' - '+this.corpName:''),
			stateful : true,
			stateId : 'xf-corp-info-pdf-win',
			width : 850,
			height : 520,
			modal : true,
			closable : true,
			maximizable : true,
			minimizable : false,
			layout : "fit",
			buttons : [{
				text : "关闭",
				handler : this.onCancel,
				scope : this
			}],
			items : {
				xtype : "mediapanel",
				mediaCfg:{
				   mediaType:'PDF',
		           url: url,
		           controls:true,
		           start:true
	        }}
		})
		
		divo.home.CorpInfoPDFWindow.superclass.initComponent.call(this);
	}
})
// EOP

;/**
 * 企业资料打印
 */
divo.home.CorpInfoHeaderForm = Ext.extend(Ext.form.FormPanel, {
	readOnly : false,
	initComponent : function() {
        
		Ext.apply(this, {
			labelWidth : 250,
			defaultType : "textfield",
			labelAlign : "right",
			autoScroll : false,
			bodyStyle : "padding:10px",
			layout : 'autotableform',
			items : [{
				fieldLabel : divo.required,
				hideLabel : true,
				xtype : 'formHtml',
				value : '请仔细核对企业资料信息，若需修改请及时联系上海消防协会消防产业委员会。',
				items : [{
					xtype : 'formBtn',
					fieldLabel : "",
					labelSeparator : "",
					text : '打印',
					handler : this.onPreview,
					scope : this
				}]
			}]
		})

		divo.home.CorpInfoHeaderForm.superclass.initComponent.call(this);
	},
	onPreview : function() {
		
		var win = new divo.home.CorpInfoPDFWindow({
			closeAction : "destroy",
			cancelAction : "destroy",
			corpId : this.corpId,
			corpName : this.corpName
		})
		win.show()
	}
	
})
Ext.reg("divo.home.CorpInfoHeaderForm", divo.home.CorpInfoHeaderForm)
// EOP

;/**
 * 检测报告操作员管理
 */
divo.home.CheckUserGridPanel = Ext.extend(divo.panel.SmartGridDetailPanel, {
	initComponent : function() {
		this.checkerId  = this.getCorpInfo().id
		
		this.action = new Ext.ux.grid.RowActions({
			header : '操作',
			autoWidth : false,
			width : 60,
			actions : [{
				iconCls : 'icon-update',
				tooltip : '修改操作员用户信息'
			}, {
				iconCls : 'icon-delete',
				tooltip : '删除操作员用户'
			}]
		})
		this.action.on('action', this.onAction, this)
		
		Ext.apply(this, {
			url : "/xf/checkusers",
			fieldDefinitions : this.getListDefinitions(),
			gridConfig : {
				gridIdOfView : 'xf-check-user-grid',
				toolbar : this.createToolbarBtn(),
				plugins : [this.action],
				displayLoadMask : false,
				footerSimple : true
			},
			autoScroll : true
		})
		
		this.subscribe("divo.home.CheckUserChanged", this.onCheckUserChanged, this)
		this.on('beforedestroy', function() {
			this.unsubscribe()
		}, this)

		divo.home.CheckUserGridPanel.superclass.initComponent.apply(this,
				arguments)
	},
	
	onAction : function(grid, record, action, row, col) {
		if (action == 'icon-update')
			this.editItem()
		if (action == 'icon-delete')
			this.deleteItem()
	},
	// 重写
	createGridMeta : function() {
		return this.getGridMeta(this.fieldDefinitions,this.isLookup?null:[this.action])
	},
	
	getListDefinitions : function() {
		return [{
			name : 'id'
		}, {
			name : 'user_id'
		}, {
			name : 'name',
			header : '用户名',
			type : 'string',
			searchable : true
		}, {
			name : 'full_name',
			header : '姓名',
			searchable : true,
			type : 'string'
		}, {
			name : 'p1',
			header:	'报告查看',
			type : 'bool',
			renderer : this.permissionRender.createDelegate(this)
		}, {
			name : 'p2',
			header:	'报告填写',
			type : 'bool',
			renderer : this.permissionRender.createDelegate(this)
		}, {
			name : 'p3',
			header : '确认报告完成',
			type : 'bool',
			renderer : this.permissionRender.createDelegate(this)
		}]
	},
	permissionRender : function(value, p, record) {
		if (value)
			return '<img src="media/images/divo/publish.gif"/>'
		return ""
	},
	onCheckUserChanged : function(subj, msg, data) {
		if (msg.sender == this.checkUserWindowId) {
			this.cIdFocus = msg.id
			this.loadList()
		}
	},
	// 添加工具条按钮
	createToolbarBtn : function() {
		var tb = [];

		var addBtn = new Ext.Toolbar.Button({
			text : '新建',
			tooltip : '新建检测报告操作员用户',
			icon : divo.iconAdd,
			cls : 'x-btn-text-icon',
			handler : this.addItem.createDelegate(this)
		})
		tb.push(addBtn)
		var resetBtn = new Ext.Toolbar.Button({
			text : '重置密码',
			tooltip : '登录密码重新设置为初始值',
			icon : divo.iconUrl + 'locked.gif',
			cls : 'x-btn-text-icon',
			handler : this.resetPassword.createDelegate(this)
		})
		tb.push(resetBtn)
		return tb
	},
	addItem : function() {
		this.checkUserWindowId = this.myId('win')
		var win = new divo.home.CheckUserAddOrEditWindow({
			id : this.checkUserWindowId,
			closeAction : "destroy",
			cancelAction : "destroy",
			adding : true,
			checkerId : this.checkerId
		})
		win.show()
	},
	editItem : function() {
		if (!this.cId) {
			this.say("请先选择要修改的操作员用户")
			return
		}
		this.checkUserWindowId = this.myId('win')

		var win = new divo.home.CheckUserAddOrEditWindow({
			id : this.checkUserWindowId,
			closeAction : "destroy",
			cancelAction : "destroy",
			adding : false,
			checkUserId : this.cId,
			checkerId : this.checkerId
		})
		win.show()
	},
	onDeleteItem : function(id) {
		var ok = false
		Ext.Ajax.request({
			scope : this,
			url : '/users/'+this.cRecordData.user_id,
			method : 'DELETE',
			async : false,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success)
					ok = true
				else
					this.say(resp.errors.reason)
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
		if (!ok) {
			return
		}
		Ext.Ajax.request({
			scope : this,
			url : '/xf/checkusers/'+id,
			method : 'DELETE',
			async : false,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success)
					this.deleteItemCallback()
				else
					this.say(resp.errors.reason)
			},
			failure : function(response, options) {
				this.alert(response.responseText)
			}
		})
	},
	resetPassword : function() {
		if (!this.cId) {
			this.say("请先选择操作员用户。")
			return
		}
		Ext.MessageBox.confirm('确认', '要重置密码吗？', this.resetPasswordConfirm
				.createDelegate(this))
	},
	resetPasswordConfirm : function(btn) {
		if (btn !== 'yes')
			return

		var ok = true
		Ext.Ajax.request({
			scope : this,
			url : "/users/" + this.cRecordData.user_id + "/password/default",
			async : false,
			method : 'PUT',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText)
				if (resp.success) {
					this.say('密码重置成功')
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)
				ok = false
			}
		});
		if (ok) {
			this.loadList()
		}
	},
	//重写
	onBeforeLoad : function(store, options) {
		store.baseParams.checkerId = this.checkerId
	},
	//重写
	render : function() {
		divo.home.CheckUserGridPanel.superclass.render.apply(this, arguments)
		this.loadList()
	}
})

Ext.reg('divo.home.CheckUserGridPanel',divo.home.CheckUserGridPanel)
//EOP
;/**
 * 检测报告操作员新建或修改窗口
 * --------------------------------------------------------------
 * 消息：
 * 检测报告操作员新增或修改了
 * 
 * 消息名：     			
 * window.xf.CheckUserChanged    
 * 
 * 消息内容：
 * {int} sender 本组件的id值
 * {int} id 新建或修改的记录id值
 * --------------------------------------------------------------
 */
divo.home.CheckUserAddOrEditWindow = Ext.extend(Ext.Window, {
	cancelAction : "hide",  //还可以取值：destroy
	closeAction : "hide",
	adding : false,
	checkUserId : null,
    checkerId : null,
	initComponent : function() {
		var btns = [{
				text : "确定",
				handler : this.onSave,
				scope : this
			}, {
				text : "取消",
				handler : this.onCancel,
				scope : this
			}]
		Ext.apply(this,{
			title : (this.adding?'新建':'修改')+'检测报告操作员',
			width : 580,
			height : 280,
			modal : true,
			stateful : true,
			stateId : 'xf-check-user-win',
			closable : true,
			maximizable : false,
			minimizable : false,
			layout : "fit",
			buttons : btns,
			items : {
				xtype : "divo.home.CheckUserAddOrEditForm",
				adding : this.adding,
				checkUserId : this.checkUserId,
				checkerId : this.checkerId
			}
		})
		
		divo.home.CheckUserAddOrEditWindow.superclass.initComponent.call(this);
	},
  	render : function() {
		divo.home.CheckUserAddOrEditWindow.superclass.render.apply(this, arguments)
		
		this.items.itemAt(0).on("clientvalidation",this.onValid,this)
	},
	onValid : function(o,valid) {
		 this.buttons[0].setDisabled(!valid)
	},
	onSave : function() {
		this.items.itemAt(0).save(this.onSaveSucccess.createDelegate(this))
	},
	onSaveSucccess : function(id) {
		this.say(this.adding ? '新建成功':'修改已经保存')
		this.close()
		
		this.publish("divo.home.CheckUserChanged",{
			sender:this.id,
			id : id
		})
	}
	
});
// EOP

;/**
 * 检测报告操作员新建或修改表单
 */
divo.home.CheckUserAddOrEditForm = Ext.extend(divo.form.AddOrEditForm, {
	initFocusFldName : 'name',
    checkUserId : null,
    checkerId : null,
	initComponent : function() {
		Ext.apply(this, {
			monitorValid : true,
			labelWidth : 100,
			defaultType : "textfield",
			labelAlign : "right",
			autoScroll : true,
			bodyStyle : "padding:10px",
			items : [{
				fieldLabel : "姓名",
				name : "full_name",
				allowBlank : false,
				width : 200
			},{
				fieldLabel : "用户名",
				name : "name",
				readOnly : !this.adding,
				width : 200,
				allowBlank : false,
				validator : function(v) {
					var t = /^[a-zA-Z0-9_\- ]+$/;
					return t.test(v);
				}
			}, {
				xtype : 'panel',
				border : false,
				bodyStyle : 'padding-left:105px;padding-bottom:20px',
				html : "即系统的登录名，只能输入英文、数字。"
			},{
				xtype : "radiogroup",
				width : 160,
				fieldLabel : "报告查看",
				disabled : true,
				items: [
                    {boxLabel: '可以', name: 'p1',inputValue: true, checked: true},
                    {boxLabel: '不可以', name: 'p1',inputValue: false}
                ]
			},{
				xtype : "radiogroup",
				width : 160,
				fieldLabel : "报告填写",
				items: [
                    {boxLabel: '可以', name: 'p2',inputValue: true, checked: true},
                    {boxLabel: '不可以', name: 'p2',inputValue: false}
                ]
			},{
				xtype : "radiogroup",
				width : 160,
				fieldLabel : "确认报告完成",
				items: [
                    {boxLabel: '可以', name: 'p3',inputValue: true, checked: true},
                    {boxLabel: '不可以', name: 'p3',inputValue: false}
                ]
			}]
		})

		divo.home.CheckUserAddOrEditForm.superclass.initComponent.call(this);
	},
	//重写
    loadItem : function() {
		Ext.Ajax.request({
			scope : this,
			url : "/xf/checkusers/"+this.checkUserId,
			method : 'GET',
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				this.getForm().setValues(resp.data)
				
				this.user_id = resp.data.user_id
			},
			failure : function(response, options) {
				this.alert(response.responseText);
			}
		})
    },
	// public
	save : function(callbackOnSuccess) {
		var item = this.getForm().getObjectValues()
		item.checker_id = this.checkerId
		item.email = ''
		item.p1 = true
		item.p2 = eval(item.p2)
		item.p3 = eval(item.p3)
		
		var userId = null
		Ext.Ajax.request({
			scope : this,
			async : false,
			url : "users"+(this.adding?"":"/"+this.user_id),
			waitMsg : "正在提交...",
			method : (this.adding?'POST':'PUT'),
			jsonData : item,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				if (!resp.success) {
					this.getForm().markInvalid(resp.errors)
				} else {
					userId = resp.id
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)	
			}
		})
		if (!userId) {
			return
		}
		item.user_id = userId
		
		Ext.Ajax.request({
			scope : this,
			async : false,
			url : "/xf/checkusers"+(this.adding?"":"/"+this.checkUserId),
			waitMsg : "正在提交...",
			method : (this.adding?'POST':'PUT'),
			jsonData : item,
			success : function(response, options) {
				var resp = Ext.decode(response.responseText);
				if (!resp.success) {
					this.getForm().markInvalid(resp.errors)
				} else {
					callbackOnSuccess(resp.id)
				}
			},
			failure : function(response, options) {
				this.alert(response.responseText)	
			}
		})
	}
})

Ext.reg("divo.home.CheckUserAddOrEditForm", divo.home.CheckUserAddOrEditForm)
// EOP

;